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
5910592
Bump knx-telegram-store to 0.14.0 (#182758)
martinhoefling Sep 20, 2026
a74b99f
Set up all Hive platforms so unload and services stay consistent (#18…
tomwilkie Sep 20, 2026
ef97d7c
Fix friends' achievement stats in Xbox integration (#182686)
scardus Sep 20, 2026
fbaee67
async_listen and async_listen_once now errors if used with run_immedi…
gjohansson-ST Sep 20, 2026
7709e2e
Bump brother to 6.2.0 (#182755)
bieniu Sep 20, 2026
eea4b0a
Bump aiomealie to 2.1.1 (#182749)
andrew-codechimp Sep 20, 2026
701da37
Update subaru integration codeowners (#182732)
G-Two Sep 20, 2026
bb7b66c
Raise HomeAssistantError when a LIFX Cloud scene fails to activate (#…
hktitof Sep 20, 2026
7826993
Fix swallowed exceptions in ifttt action handlers (#182747)
rasadregmi Sep 20, 2026
94a4a22
Add binary sensors to NexBlue integration (#182572)
nexblue-maintainer Sep 20, 2026
123a469
Add UniFi Protect numbers to the API key only mode (#182726)
RaHehl Sep 20, 2026
125bcf4
New: Threema Integration (#165993)
LukasQ Sep 20, 2026
20cadab
Add a new integration for Theben Conexa smartmeter gateway (#174407)
mdluhosch Sep 20, 2026
17edc35
Use OpenAPI 3.1.0 format for ovhcloud_ai_endpoints (#182770)
Shulyaka Sep 20, 2026
97be9d1
Add reauthentication to Monarch Money (#182773)
bradleyseanf Sep 20, 2026
001abba
Use OpenAPI 3.1.0 format for MCP Server JSON schemas (#182767)
Shulyaka Sep 20, 2026
e99a729
Enable credential autofill for Monarch Money (#182772)
bradleyseanf Sep 20, 2026
1439024
Normalize test fixtures for irm_kmi (#182692)
YP30 Sep 20, 2026
c58924c
Use OpenAPI 3.1.0 format for Ollama JSON schemas (#182761)
Shulyaka Sep 20, 2026
96ff6c0
Fix templated Mealie mealplan ID handling (#182615)
elp3dr0 Sep 20, 2026
113db1b
Mark Monzo as Platinum quality (#180813)
JakeMartin-ICL Sep 20, 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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ homeassistant.components.teltonika.*
homeassistant.components.teslemetry.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
homeassistant.components.threema.*
homeassistant.components.threshold.*
homeassistant.components.tibber.*
homeassistant.components.tile.*
Expand Down
8 changes: 6 additions & 2 deletions CODEOWNERS

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

2 changes: 1 addition & 1 deletion homeassistant/components/brother/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"iot_class": "local_polling",
"loggers": ["brother", "pyasn1", "pysmi", "pysnmp"],
"quality_scale": "platinum",
"requirements": ["brother==6.1.2"],
"requirements": ["brother==6.2.0"],
"zeroconf": [
{
"name": "brother*",
Expand Down
11 changes: 2 additions & 9 deletions homeassistant/components/hive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from homeassistant.helpers import aiohttp_client, device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_send

from .const import DOMAIN, PLATFORM_LOOKUP, PLATFORMS
from .const import DOMAIN, PLATFORMS
from .entity import HiveEntity

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -60,14 +60,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: HiveConfigEntry) -> bool
manufacturer=hub_data["deviceData"]["manufacturer"],
)

await hass.config_entries.async_forward_entry_setups(
entry,
[
ha_type
for ha_type, hive_type in PLATFORM_LOOKUP.items()
if devices.get(hive_type)
],
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/hive/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ async def async_setup_entry(

sensors: list[BinarySensorEntity] = []

devices = hive.session.deviceList.get("binary_sensor")
devices = hive.session.deviceList.get("binary_sensor", [])
sensors.extend(
HiveBinarySensorEntity(hass, entry, hive, dev, description)
for dev in devices
for description in BINARY_SENSOR_TYPES
if dev["hiveType"] == description.key
)

devices = hive.session.deviceList.get("sensor")
devices = hive.session.deviceList.get("sensor", [])
sensors.extend(
HiveSensorEntity(hass, entry, hive, dev, description)
for dev in devices
Expand Down
8 changes: 0 additions & 8 deletions homeassistant/components/hive/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@
Platform.SWITCH,
Platform.WATER_HEATER,
]
PLATFORM_LOOKUP = {
Platform.BINARY_SENSOR: "binary_sensor",
Platform.CLIMATE: "climate",
Platform.LIGHT: "light",
Platform.SENSOR: "sensor",
Platform.SWITCH: "switch",
Platform.WATER_HEATER: "water_heater",
}
SERVICE_BOOST_HOT_WATER = "boost_hot_water"
SERVICE_BOOST_HEATING_ON = "boost_heating_on"
SERVICE_BOOST_HEATING_OFF = "boost_heating_off"
Expand Down
9 changes: 6 additions & 3 deletions homeassistant/components/ifttt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_WEBHOOK_ID
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_entry_flow, config_validation as cv
from homeassistant.helpers.typing import ConfigType

Expand Down Expand Up @@ -86,9 +87,11 @@ def trigger_service(call: ServiceCall) -> None:
res = pyfttt.send_event(key, event, value1, value2, value3)
if res.status_code != HTTPStatus.OK:
_LOGGER.error("IFTTT reported error sending event to %s", target)
# pylint: disable-next=home-assistant-action-swallowed-exception
except requests.exceptions.RequestException:
_LOGGER.exception("Error communicating with IFTTT")
except requests.exceptions.RequestException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="trigger_failed",
) from err

hass.services.async_register(
DOMAIN, SERVICE_TRIGGER, trigger_service, schema=SERVICE_TRIGGER_SCHEMA
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/ifttt/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
}
}
},
"exceptions": {
"trigger_failed": {
"message": "Failed to trigger the IFTTT webhook."
}
},
"services": {
"push_alarm_state": {
"description": "Updates the alarm state to the specified value.",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/knx/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"xknx==3.20.0",
"xknxproject==3.10.0",
"knx-frontend==2026.9.4.63549",
"knx-telegram-store[sqlite,postgres]==0.11.2"
"knx-telegram-store[sqlite,postgres]==0.14.0"
],
"single_config_entry": true
}
5 changes: 5 additions & 0 deletions homeassistant/components/knx/telegrams.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ def __init__(
retention_days=self.retention_days,
flush_interval=FLUSH_INTERVAL_SECONDS_SQLITE,
max_buffer_size=MAX_BUFFER_TELEGRAMS_SQLITE,
# Databases written before knx-telegram-store 0.14 hold local
# wall-clock times with no offset. Only we know which zone wrote
# them - it is ours, not necessarily the host's - so the store
# is told once and converts them on this start.
legacy_timestamp_timezone=dt_util.get_default_time_zone(),
)

self._xknx_telegram_cb_handle = (
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/lifx_cloud/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Constants for the LIFX Cloud integration."""

DOMAIN = "lifx_cloud"
12 changes: 9 additions & 3 deletions homeassistant/components/lifx_cloud/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
from homeassistant.components.scene import Scene
from homeassistant.const import CONF_PLATFORM, CONF_TIMEOUT, CONF_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

DEFAULT_TIMEOUT = 10
Expand Down Expand Up @@ -93,6 +96,9 @@ async def async_activate(self, **kwargs: Any) -> None:
async with asyncio.timeout(self._timeout):
await httpsession.put(url, headers=self._headers)

# pylint: disable-next=home-assistant-action-swallowed-exception
except TimeoutError, aiohttp.ClientError:
_LOGGER.exception("Error on %s", url)
except (TimeoutError, aiohttp.ClientError) as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="activate_failed",
translation_placeholders={"error": str(err)},
) from err
7 changes: 7 additions & 0 deletions homeassistant/components/lifx_cloud/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"exceptions": {
"activate_failed": {
"message": "Failed to activate the LIFX Cloud scene: {error}"
}
}
}
4 changes: 3 additions & 1 deletion homeassistant/components/mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def _format_tool(
) -> types.Tool:
"""Format tool specification."""
input_schema = probatio.to_openapi(
tool.parameters, custom_serializer=custom_serializer
tool.parameters,
custom_serializer=custom_serializer,
openapi_version="3.1.0",
)
mcp_schema: dict[str, Any] = {
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mealie/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["aiomealie==2.0.0"]
"requirements": ["aiomealie==2.1.1"]
}
14 changes: 7 additions & 7 deletions homeassistant/components/mealie/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@
SERVICE_DELETE_MEALPLAN_SCHEMA = probatio.Schema(
{
probatio.Required(ATTR_CONFIG_ENTRY_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): probatio.Any(str, int),
}
)
SERVICE_UPDATE_MEALPLAN = "update_mealplan"
SERVICE_UPDATE_MEALPLAN_SCHEMA = probatio.Any(
probatio.Schema(
{
probatio.Required(ATTR_CONFIG_ENTRY_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): probatio.Any(str, int),
probatio.Required(ATTR_DATE): cv.date,
probatio.Required(ATTR_ENTRY_TYPE): probatio.In(
[x.lower() for x in MealplanEntryType]
Expand All @@ -135,7 +135,7 @@
probatio.Schema(
{
probatio.Required(ATTR_CONFIG_ENTRY_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): str,
probatio.Required(ATTR_MEALPLAN_ID): probatio.Any(str, int),
probatio.Required(ATTR_DATE): cv.date,
probatio.Required(ATTR_ENTRY_TYPE): probatio.In(
[x.lower() for x in MealplanEntryType]
Expand Down Expand Up @@ -319,7 +319,7 @@ async def _async_delete_mealplan(call: ServiceCall) -> ServiceResponse:
entry: MealieConfigEntry = service.async_get_config_entry(
call.hass, DOMAIN, call.data[ATTR_CONFIG_ENTRY_ID]
)
mealplan_id = call.data[ATTR_MEALPLAN_ID]
mealplan_id = int(call.data[ATTR_MEALPLAN_ID])
client = entry.runtime_data.client

try:
Expand All @@ -335,7 +335,7 @@ async def _async_delete_mealplan(call: ServiceCall) -> ServiceResponse:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="mealplan_not_found",
translation_placeholders={"mealplan_id": mealplan_id},
translation_placeholders={"mealplan_id": str(mealplan_id)},
) from err
return None

Expand All @@ -345,7 +345,7 @@ async def _async_update_mealplan(call: ServiceCall) -> ServiceResponse:
entry: MealieConfigEntry = service.async_get_config_entry(
call.hass, DOMAIN, call.data[ATTR_CONFIG_ENTRY_ID]
)
mealplan_id = call.data[ATTR_MEALPLAN_ID]
mealplan_id = int(call.data[ATTR_MEALPLAN_ID])
mealplan_date = call.data[ATTR_DATE]
entry_type = MealplanEntryType(call.data[ATTR_ENTRY_TYPE])
client = entry.runtime_data.client
Expand All @@ -370,7 +370,7 @@ async def _async_update_mealplan(call: ServiceCall) -> ServiceResponse:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="mealplan_not_found",
translation_placeholders={"mealplan_id": mealplan_id},
translation_placeholders={"mealplan_id": str(mealplan_id)},
) from err
if call.return_response:
return {"mealplan": asdict(mealplan)}
Expand Down
53 changes: 48 additions & 5 deletions homeassistant/components/monarch_money/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"""Config flow for Monarch Money integration."""

from collections.abc import Mapping
import logging
from typing import Any, override

from aiohttp import ClientError, ClientResponseError
from gql.transport.exceptions import TransportError, TransportServerError
from monarchmoney import LoginFailedException, RequireMFAException
from monarchmoney.monarchmoney import SESSION_FILE
import probatio
from typedmonarchmoney import TypedMonarchMoney
from typedmonarchmoney.models import MonarchSubscription

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_EMAIL, CONF_ID, CONF_PASSWORD, CONF_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
Expand All @@ -29,11 +32,13 @@
probatio.Required(CONF_EMAIL): TextSelector(
TextSelectorConfig(
type=TextSelectorType.EMAIL,
autocomplete="username",
),
),
probatio.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
),
),
}
Expand Down Expand Up @@ -112,6 +117,24 @@ async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
return await self._async_step_login("user", user_input)

async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Start reauthentication after credentials expire."""
return await self.async_step_reauth_confirm()

async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Ask for credentials to renew the existing subscription token."""
return await self._async_step_login("reauth_confirm", user_input)

async def _async_step_login(
self, step_id: str, user_input: dict[str, Any] | None
) -> ConfigFlowResult:
"""Authenticate a new or existing entry, including MFA."""
errors: dict[str, str] = {}

if user_input is not None:
Expand All @@ -124,28 +147,48 @@ async def async_step_user(
self.password = user_input[CONF_PASSWORD]

return self.async_show_form(
step_id="user",
step_id=step_id,
data_schema=STEP_MFA_DATA_SCHEMA,
errors={"base": "mfa_required"},
)
except BadMFA:
return self.async_show_form(
step_id="user",
step_id=step_id,
data_schema=STEP_MFA_DATA_SCHEMA,
errors={"base": "bad_mfa"},
)
except InvalidAuth:
except InvalidAuth, LoginFailedException:
self.email = self.password = None
errors["base"] = "invalid_auth"
except (ClientResponseError, TransportServerError) as err:
status = (
err.status if isinstance(err, ClientResponseError) else err.code
)
if status in (401, 403):
self.email = self.password = None
errors["base"] = "invalid_auth"
else:
errors["base"] = "cannot_connect"
except ClientError, TransportError, TimeoutError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(info[CONF_ID])
if self.source == SOURCE_REAUTH:
self._abort_if_unique_id_mismatch()
return self.async_update_reload_and_abort(
self._get_reauth_entry(),
data_updates={CONF_TOKEN: info[CONF_TOKEN]},
)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title="Monarch Money",
data={CONF_TOKEN: info[CONF_TOKEN]},
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
step_id=step_id,
data_schema=STEP_MFA_DATA_SCHEMA if self.email else STEP_USER_DATA_SCHEMA,
errors=errors,
)


Expand Down
Loading
Loading