Compare commits

...

11 Commits

Author SHA1 Message Date
Erik 48d67a279c Update mobile_app test 2026-05-27 09:45:05 +02:00
Erik a7253710fa Make TrackerEntity in_zones win over lat/long 2026-05-27 08:54:51 +02:00
Jan Bouwhuis 7adaa09333 Add override decorator for incomfort to comply with PEP 698 (#172244) 2026-05-27 08:20:16 +02:00
tronikos c5e7ed9aba Update recommended chat model to gemini-3.1-flash-lite (#172299) 2026-05-27 08:19:01 +02:00
Max Michels 68b8667998 Add missing exception translation key in aws_s3 (#172270) 2026-05-27 07:31:58 +02:00
J. Nick Koston f643dd98e5 Bump habluetooth to 6.7.9 (#172303) 2026-05-26 23:55:04 -05:00
J. Nick Koston dcec29dbbf Bump qingping-ble to 1.1.5 (#172305) 2026-05-26 22:41:55 -05:00
J. Nick Koston 1daff77591 Skip Linux only bluetooth scanner tests on non Linux platforms (#172304) 2026-05-26 22:41:41 -05:00
Yardian Support 7e3fc18c8c Update Yardian codeowners to @aeon-matrix (#172273) 2026-05-26 19:04:47 -05:00
J. Nick Koston b6cc5499aa Bump dbus-fast to 5.0.15 (#172298) 2026-05-26 19:00:28 -05:00
Manu 11920b82fe Fix typo in System Bridge (#172294)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-27 01:58:34 +02:00
24 changed files with 81 additions and 41 deletions
Generated
+2 -2
View File
@@ -2054,8 +2054,8 @@ CLAUDE.md @home-assistant/core
/tests/components/yamaha_musiccast/ @vigonotion @micha91
/homeassistant/components/yandex_transport/ @rishatik92 @devbis
/tests/components/yandex_transport/ @rishatik92 @devbis
/homeassistant/components/yardian/ @h3l1o5
/tests/components/yardian/ @h3l1o5
/homeassistant/components/yardian/ @aeon-matrix
/tests/components/yardian/ @aeon-matrix
/homeassistant/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015
/tests/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015
/homeassistant/components/yeelightsunflower/ @lindsaymarkward
@@ -51,7 +51,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
translation_key="invalid_bucket_name",
) from err
except ValueError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_endpoint_url",
+4 -1
View File
@@ -7,7 +7,7 @@
"cannot_connect": "[%key:component::aws_s3::exceptions::cannot_connect::message%]",
"invalid_bucket_name": "[%key:component::aws_s3::exceptions::invalid_bucket_name::message%]",
"invalid_credentials": "[%key:component::aws_s3::exceptions::invalid_credentials::message%]",
"invalid_endpoint_url": "Invalid endpoint URL. Please make sure it's a valid AWS S3 endpoint URL."
"invalid_endpoint_url": "[%key:component::aws_s3::exceptions::invalid_endpoint_url::message%]"
},
"step": {
"user": {
@@ -48,6 +48,9 @@
},
"invalid_credentials": {
"message": "Bucket cannot be accessed using provided combination of access key ID and secret access key."
},
"invalid_endpoint_url": {
"message": "Invalid endpoint URL. Please make sure it's a valid AWS S3 endpoint URL."
}
}
}
@@ -20,7 +20,7 @@
"bluetooth-adapters==2.3.0",
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.14",
"habluetooth==6.7.4"
"dbus-fast==5.0.15",
"habluetooth==6.7.9"
]
}
@@ -221,8 +221,8 @@ class TrackerEntity(
"""Return the entity_id of zones the device is currently in.
The list may be in any order; the base class sorts it by zone radius
and discards zones which do not exist. Ignored if latitude and
longitude are both set.
and discards zones which do not exist. Takes precedence over latitude
and longitude when set (including when set to an empty list).
"""
return self._attr_in_zones
@@ -252,11 +252,7 @@ class TrackerEntity(
@callback
def _async_write_ha_state(self) -> None:
"""Calculate active zones."""
if self.available and self.latitude is not None and self.longitude is not None:
self.__active_zone, self.__in_zones = zone.async_in_zones(
self.hass, self.latitude, self.longitude, self.location_accuracy
)
elif (zones := self.in_zones) is not None:
if (zones := self.in_zones) is not None:
zone_states = sorted(
(
zone_state
@@ -270,6 +266,12 @@ class TrackerEntity(
None,
)
self.__in_zones = [z.entity_id for z in zone_states]
elif (
self.available and self.latitude is not None and self.longitude is not None
):
self.__active_zone, self.__in_zones = zone.async_in_zones(
self.hass, self.latitude, self.longitude, self.location_accuracy
)
else:
self.__active_zone = None
self.__in_zones = None
@@ -19,7 +19,7 @@ DEFAULT_STT_PROMPT = "Transcribe the attached audio"
CONF_RECOMMENDED = "recommended"
CONF_CHAT_MODEL = "chat_model"
RECOMMENDED_CHAT_MODEL = "models/gemini-2.5-flash"
RECOMMENDED_CHAT_MODEL = "models/gemini-3.1-flash-lite"
RECOMMENDED_STT_MODEL = RECOMMENDED_CHAT_MODEL
RECOMMENDED_TTS_MODEL = "models/gemini-2.5-flash-preview-tts"
RECOMMENDED_IMAGE_MODEL = "models/gemini-2.5-flash-image"
@@ -2,7 +2,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from typing import Any, override
from incomfortclient import Heater as InComfortHeater
@@ -97,11 +97,13 @@ class IncomfortBinarySensor(IncomfortBoilerEntity, BinarySensorEntity):
self._attr_unique_id = f"{heater.serial_no}_{description.key}"
@property
@override
def is_on(self) -> bool:
"""Return the status of the sensor."""
return bool(self._heater.status[self.entity_description.value_key])
@property
@override
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the device state attributes."""
if (attributes_fn := self.entity_description.extra_state_attributes_fn) is None:
@@ -1,6 +1,6 @@
"""Support for an Intergas boiler via an InComfort/InTouch Lan2RF gateway."""
from typing import Any
from typing import Any, override
from incomfortclient import Heater as InComfortHeater, Room as InComfortRoom
@@ -76,16 +76,19 @@ class InComfortClimate(IncomfortEntity, ClimateEntity):
)
@property
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
return {"status": self._room.status}
@property
@override
def current_temperature(self) -> float | None:
"""Return the current temperature."""
return self._room.room_temp
@property
@override
def hvac_action(self) -> HVACAction | None:
"""Return the actual current HVAC action."""
if self._heater.is_burning and self._heater.is_pumping:
@@ -93,6 +96,7 @@ class InComfortClimate(IncomfortEntity, ClimateEntity):
return HVACAction.IDLE
@property
@override
def target_temperature(self) -> float | None:
"""Return the (override)temperature we try to reach.
@@ -106,11 +110,13 @@ class InComfortClimate(IncomfortEntity, ClimateEntity):
return self._room.setpoint
return self._room.override or self._room.setpoint
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set a new target temperature for this zone."""
temperature: float = kwargs[ATTR_TEMPERATURE]
await self._room.set_override(temperature)
await self.coordinator.async_refresh()
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
@@ -2,7 +2,7 @@
from collections.abc import Mapping
import logging
from typing import Any
from typing import Any, override
from incomfortclient import InvalidGateway, InvalidHeaterList
import voluptuous as vol
@@ -100,6 +100,7 @@ class InComfortConfigFlow(ConfigFlow, domain=DOMAIN):
_discovered_host: str
@override
@staticmethod
@callback
def async_get_options_flow(
@@ -108,6 +109,7 @@ class InComfortConfigFlow(ConfigFlow, domain=DOMAIN):
"""Get the options flow for this handler."""
return InComfortOptionsFlowHandler()
@override
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
@@ -169,6 +171,7 @@ class InComfortConfigFlow(ConfigFlow, domain=DOMAIN):
description_placeholders={CONF_HOST: self._discovered_host},
)
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -3,7 +3,7 @@
from dataclasses import dataclass, field
from datetime import timedelta
import logging
from typing import Any
from typing import Any, override
from aiohttp import ClientResponseError
from incomfortclient import (
@@ -74,6 +74,7 @@ class InComfortDataCoordinator(DataUpdateCoordinator[InComfortData]):
)
self.incomfort_data = incomfort_data
@override
async def _async_update_data(self) -> InComfortData:
"""Fetch data from API endpoint."""
try:
+3 -1
View File
@@ -1,7 +1,7 @@
"""Support for an Intergas heater via an InComfort/InTouch Lan2RF gateway."""
from dataclasses import dataclass
from typing import Any
from typing import Any, override
from incomfortclient import Heater as InComfortHeater
@@ -104,11 +104,13 @@ class IncomfortSensor(IncomfortBoilerEntity, SensorEntity):
self._attr_unique_id = f"{heater.serial_no}_{description.key}"
@property
@override
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self._heater.status[self.entity_description.value_key] # type: ignore [no-any-return]
@property
@override
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the device state attributes."""
if (extra_key := self.entity_description.extra_key) is None:
@@ -1,7 +1,7 @@
"""Support for an Intergas boiler via an InComfort/Intouch Lan2RF gateway."""
import logging
from typing import Any
from typing import Any, override
from incomfortclient import Heater as InComfortHeater
@@ -49,11 +49,13 @@ class IncomfortWaterHeater(IncomfortBoilerEntity, WaterHeaterEntity):
self._attr_unique_id = heater.serial_no
@property
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
return {k: v for k, v in self._heater.status.items() if k in HEATER_ATTRS}
@property
@override
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if self._heater.is_tapping:
@@ -67,6 +69,7 @@ class IncomfortWaterHeater(IncomfortBoilerEntity, WaterHeaterEntity):
return max(self._heater.heater_temp, self._heater.tap_temp)
@property
@override
def current_operation(self) -> str | None:
"""Return the current operation mode."""
return self._heater.display_text
@@ -21,5 +21,5 @@
"documentation": "https://www.home-assistant.io/integrations/qingping",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["qingping-ble==1.1.4"]
"requirements": ["qingping-ble==1.1.5"]
}
@@ -217,9 +217,9 @@
"home": "[%key:common::entity::button::home::name%]",
"insert": "Insert",
"left": "[%key:common::entity::button::left::name%]",
"lights_kbd_down": "Keyboasrd backlight brightness down",
"lights_kbd_down": "Keyboard backlight brightness down",
"lights_kbd_toggle": "Toggle keyboard backlight",
"lights_kbd_up": "Keyboard backlight brighness up",
"lights_kbd_up": "Keyboard backlight brightness up",
"lights_mon_down": "Display brightness down",
"lights_mon_up": "Display brightness up",
"numpad_0": "NumPad 0",
@@ -1,7 +1,7 @@
{
"domain": "yardian",
"name": "Yardian",
"codeowners": ["@h3l1o5"],
"codeowners": ["@aeon-matrix"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/yardian",
"integration_type": "device",
+2 -2
View File
@@ -30,12 +30,12 @@ certifi>=2021.5.30
ciso8601==2.3.3
cronsim==2.7
cryptography==48.0.0
dbus-fast==5.0.14
dbus-fast==5.0.15
file-read-backwards==2.0.0
fnv-hash-fast==2.0.3
go2rtc-client==0.4.0
ha-ffmpeg==3.2.2
habluetooth==6.7.4
habluetooth==6.7.9
hass-nabucasa==2.2.0
hassil==3.5.0
home-assistant-bluetooth==2.0.0
+3 -3
View File
@@ -797,7 +797,7 @@ datadog==0.52.0
datapoint==0.12.1
# homeassistant.components.bluetooth
dbus-fast==5.0.14
dbus-fast==5.0.15
# homeassistant.components.debugpy
debugpy==1.8.17
@@ -1213,7 +1213,7 @@ ha-xthings-cloud==1.0.5
habiticalib==0.4.7
# homeassistant.components.bluetooth
habluetooth==6.7.4
habluetooth==6.7.9
# homeassistant.components.hanna
hanna-cloud==0.0.7
@@ -2839,7 +2839,7 @@ qbittorrent-api==2026.5.1
qbusmqttapi==1.5.0
# homeassistant.components.qingping
qingping-ble==1.1.4
qingping-ble==1.1.5
# homeassistant.components.qnap
qnapstats==0.4.0
+3
View File
@@ -2,6 +2,7 @@
import asyncio
from datetime import timedelta
import sys
import time
from typing import Any
from unittest.mock import AsyncMock, MagicMock, Mock, patch
@@ -99,6 +100,7 @@ async def test_setup_and_stop(
assert len(mock_bleak_scanner_start.mock_calls) == 1
@pytest.mark.skipif(sys.platform != "linux", reason="Requires Linux BlueZ scanner")
@pytest.mark.parametrize(
"options",
[{CONF_MODE: "passive"}, {CONF_PASSIVE: True}],
@@ -161,6 +163,7 @@ async def test_setup_and_stop_passive(
}
@pytest.mark.skipif(sys.platform != "linux", reason="Requires Linux BlueZ scanner")
async def test_setup_and_stop_old_bluez(
hass: HomeAssistant,
mock_bleak_scanner_start: MagicMock,
+19 -3
View File
@@ -683,15 +683,31 @@ async def test_load_unload_entry_tracker(
None,
1.0,
2.0,
STATE_HOME,
{
ATTR_SOURCE_TYPE: SourceType.GPS,
ATTR_GPS_ACCURACY: 0,
ATTR_IN_ZONES: ["zone.home"],
ATTR_LATITUDE: 1.0,
ATTR_LONGITUDE: 2.0,
},
id="in_zones_wins_over_lat_long",
),
pytest.param(
None,
[],
None,
50.0,
60.0,
STATE_NOT_HOME,
{
ATTR_SOURCE_TYPE: SourceType.GPS,
ATTR_GPS_ACCURACY: 0,
ATTR_IN_ZONES: [],
ATTR_LATITUDE: 1.0,
ATTR_LONGITUDE: 2.0,
ATTR_LATITUDE: 50.0,
ATTR_LONGITUDE: 60.0,
},
id="in_zones_ignored_when_lat_long_set",
id="empty_in_zones_wins_over_lat_long",
),
pytest.param(
None,
@@ -18,7 +18,7 @@
}),
'ulid-conversation': dict({
'data': dict({
'chat_model': 'models/gemini-2.5-flash',
'chat_model': 'models/gemini-3.1-flash-lite',
'dangerous_block_threshold': 'BLOCK_MEDIUM_AND_ABOVE',
'harassment_block_threshold': 'BLOCK_MEDIUM_AND_ABOVE',
'hate_block_threshold': 'BLOCK_MEDIUM_AND_ABOVE',
@@ -21,7 +21,7 @@
'labels': set({
}),
'manufacturer': 'Google',
'model': 'gemini-2.5-flash',
'model': 'gemini-3.1-flash-lite',
'model_id': None,
'name': 'Google AI Conversation',
'name_by_user': None,
@@ -50,7 +50,7 @@
'labels': set({
}),
'manufacturer': 'Google',
'model': 'gemini-2.5-flash',
'model': 'gemini-3.1-flash-lite',
'model_id': None,
'name': 'Google AI STT',
'name_by_user': None,
@@ -108,7 +108,7 @@
'labels': set({
}),
'manufacturer': 'Google',
'model': 'gemini-2.5-flash',
'model': 'gemini-3.1-flash-lite',
'model_id': None,
'name': 'Google AI Task',
'name_by_user': None,
@@ -51,7 +51,7 @@ def get_models_pager():
model_25_flash = Mock(
supported_actions=["generateContent"],
)
model_25_flash.name = "models/gemini-2.5-flash"
model_25_flash.name = "models/gemini-3.1-flash-lite"
model_20_flash = Mock(
supported_actions=["generateContent"],
@@ -21,7 +21,7 @@ from . import API_ERROR_500, CLIENT_ERROR_BAD_REQUEST
from tests.common import MockConfigEntry
TEST_CHAT_MODEL = "models/gemini-2.5-flash"
TEST_CHAT_MODEL = "models/gemini-3.1-flash-lite"
TEST_PROMPT = "Please transcribe the audio."
@@ -172,16 +172,16 @@ async def setup_zone(hass: HomeAssistant) -> None:
{"in_zones": []},
"not_home",
),
# in_zones + gps: gps wins, in_zones recomputed from coordinates
# in_zones + gps: in_zones wins, gps coordinates still reported as attributes
(
{"gps": [10, 20], "in_zones": ["zone.school"]},
{
"latitude": 10,
"longitude": 20,
"gps_accuracy": 30,
"in_zones": ["zone.home"],
"in_zones": ["zone.school"],
},
"home",
"School",
),
],
)