Compare commits

...

18 Commits

Author SHA1 Message Date
farmio
9d979a1d7f review; mark disabled by default 2025-12-03 20:09:09 +01:00
farmio
0a1214256a Add counter for KNX DataSecure undecodable telegrams 2025-12-03 14:52:04 +01:00
Artur Pragacz
1a60c46d67 Bump aioonkyo to 0.4.0 (#157838) 2025-12-03 14:46:52 +01:00
Matthias Alphart
62fba5ca20 Update xknx to 3.12.0 (#157835) 2025-12-03 14:40:40 +01:00
victorigualada
b54cde795c Bump hass-nabucasa from 1.6.2 to 1.7.0 (#157834) 2025-12-03 14:37:45 +01:00
victorigualada
0f456373bf Allow non strict response_format structures for Cloud LLM generation (#157822) 2025-12-03 14:31:09 +01:00
IAmStiven
a5042027b8 Add support for new ElevenLabs model Scribe v2 (#156961) 2025-12-03 14:29:25 +01:00
Franck Nijhof
b15b5ba95c Add final learn more and feedback links for purpose-specific triggers and conditions preview feature (#157830) 2025-12-03 13:14:37 +01:00
Robert Resch
cd6e72798e Prioritize default stun port over alternative (#157829) 2025-12-03 13:14:28 +01:00
Kamil Breguła
739157e59f Simplify availability property in WLED (#157800)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-03 13:00:21 +01:00
torben-iometer
267aa1af42 bump iometer to v0.3.0 (#157826) 2025-12-03 12:47:05 +01:00
Michael
7328b61a69 Add integration_type to Oralb (#157828) 2025-12-03 12:46:50 +01:00
Allen Porter
203f2fb364 Bump google-nest-sdm to 9.1.2 (#157812)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-12-03 11:23:00 +01:00
Josef Zweck
b956c17ce4 Mark nordpool as service integration_type (#157810) 2025-12-03 11:22:42 +01:00
Marc Mueller
5163dc0567 Fix ping TypeError when killing the process (#157794) 2025-12-03 11:22:14 +01:00
Allen Porter
31a0478717 Bump python-roborock to 3.9.2 (#157815)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-12-03 10:56:56 +01:00
dependabot[bot]
24da3f0db8 Bump actions/checkout from 6.0.0 to 6.0.1 (#157806)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 10:38:45 +01:00
dependabot[bot]
786922fc5d Bump actions/stale from 10.1.0 to 10.1.1 (#157807)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 10:36:44 +01:00
39 changed files with 174 additions and 87 deletions

View File

@@ -30,7 +30,7 @@ jobs:
architectures: ${{ env.ARCHITECTURES }}
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
@@ -96,7 +96,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
@@ -273,7 +273,7 @@ jobs:
- green
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set build additional args
run: |
@@ -311,7 +311,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
@@ -464,7 +464,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
@@ -509,7 +509,7 @@ jobs:
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0

View File

@@ -99,7 +99,7 @@ jobs:
steps:
- &checkout
name: Check out code from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Generate partial Python venv restore key
id: generate_python_cache_key
run: |

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Initialize CodeQL
uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6

View File

@@ -17,7 +17,7 @@ jobs:
# - No PRs marked as no-stale
# - No issues (-1)
- name: 60 days stale PRs policy
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
@@ -57,7 +57,7 @@ jobs:
# - No issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: 90 days stale issues
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
repo-token: ${{ steps.token.outputs.token }}
days-before-stale: 90
@@ -87,7 +87,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
repo-token: ${{ steps.token.outputs.token }}
only-labels: "needs-more-information"

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0

View File

@@ -31,7 +31,7 @@ jobs:
steps:
- &checkout
name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python

View File

@@ -8,6 +8,8 @@
"integration_type": "system",
"preview_features": {
"new_triggers_conditions": {
"feedback_url": "https://forms.gle/fWFZqf5MzuwWTsCH8",
"learn_more_url": "https://www.home-assistant.io/blog/2025/12/03/release-202512/#purpose-specific-triggers-and-conditions",
"report_issue_url": "https://github.com/home-assistant/core/issues/new?template=bug_report.yml&integration_link=https://www.home-assistant.io/integrations/automation&integration_name=Automation"
}
},

View File

@@ -407,8 +407,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return [
RTCIceServer(
urls=[
"stun:stun.home-assistant.io:80",
"stun:stun.home-assistant.io:3478",
"stun:stun.home-assistant.io:80",
]
),
]

View File

@@ -561,7 +561,7 @@ class BaseCloudLLMEntity(Entity):
"schema": _format_structured_output(
structure, chat_log.llm_api
),
"strict": True,
"strict": False,
},
}

View File

@@ -13,6 +13,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["acme", "hass_nabucasa", "snitun"],
"requirements": ["hass-nabucasa==1.6.2"],
"requirements": ["hass-nabucasa==1.7.0"],
"single_config_entry": true
}

View File

@@ -17,7 +17,7 @@ DEFAULT_TTS_MODEL = "eleven_multilingual_v2"
DEFAULT_STABILITY = 0.5
DEFAULT_SIMILARITY = 0.75
DEFAULT_STT_AUTO_LANGUAGE = False
DEFAULT_STT_MODEL = "scribe_v1"
DEFAULT_STT_MODEL = "scribe_v2"
DEFAULT_STYLE = 0
DEFAULT_USE_SPEAKER_BOOST = True
@@ -129,4 +129,5 @@ STT_LANGUAGES = [
STT_MODELS = {
"scribe_v1": "Scribe v1",
"scribe_v1_experimental": "Scribe v1 Experimental",
"scribe_v2": "Scribe v2 Realtime",
}

View File

@@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["iometer==0.2.0"],
"requirements": ["iometer==0.3.0"],
"zeroconf": ["_iometer._tcp.local."]
}

View File

@@ -21,6 +21,9 @@
"telegram_count": {
"default": "mdi:plus-network"
},
"telegrams_data_secure_undecodable": {
"default": "mdi:lock-alert"
},
"telegrams_incoming": {
"default": "mdi:upload-network"
},

View File

@@ -11,7 +11,7 @@
"loggers": ["xknx", "xknxproject"],
"quality_scale": "silver",
"requirements": [
"xknx==3.11.0",
"xknx==3.12.0",
"xknxproject==3.8.2",
"knx-frontend==2025.10.31.195356"
],

View File

@@ -108,6 +108,12 @@ SYSTEM_ENTITY_DESCRIPTIONS = (
+ knx.xknx.connection_manager.cemi_count_incoming
+ knx.xknx.connection_manager.cemi_count_incoming_error,
),
KNXSystemEntityDescription(
key="telegrams_data_secure_undecodable",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda knx: knx.xknx.connection_manager.undecoded_data_secure,
),
)

View File

@@ -639,6 +639,10 @@
"name": "Telegrams",
"unit_of_measurement": "telegrams"
},
"telegrams_data_secure_undecodable": {
"name": "Undecodable Data Secure telegrams",
"unit_of_measurement": "[%key:component::knx::entity::sensor::telegrams_incoming_error::unit_of_measurement%]"
},
"telegrams_incoming": {
"name": "Incoming telegrams",
"unit_of_measurement": "[%key:component::knx::entity::sensor::telegram_count::unit_of_measurement%]"

View File

@@ -19,5 +19,5 @@
"documentation": "https://www.home-assistant.io/integrations/nest",
"iot_class": "cloud_push",
"loggers": ["google_nest_sdm"],
"requirements": ["google-nest-sdm==9.1.1"]
"requirements": ["google-nest-sdm==9.1.2"]
}

View File

@@ -4,7 +4,7 @@
"codeowners": ["@gjohansson-ST"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/nordpool",
"integration_type": "hub",
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["pynordpool"],
"quality_scale": "platinum",

View File

@@ -9,7 +9,7 @@
"iot_class": "local_push",
"loggers": ["aioonkyo"],
"quality_scale": "bronze",
"requirements": ["aioonkyo==0.3.0"],
"requirements": ["aioonkyo==0.4.0"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",

View File

@@ -10,6 +10,7 @@
"config_flow": true,
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/oralb",
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["oralb_ble"],
"requirements": ["oralb-ble==0.17.6"]

View File

@@ -162,7 +162,7 @@ class PingDataSubProcess(PingData):
if pinger:
with suppress(TypeError, ProcessLookupError):
await pinger.kill() # type: ignore[func-returns-value]
pinger.kill()
del pinger
return None

View File

@@ -20,7 +20,7 @@
"loggers": ["roborock"],
"quality_scale": "silver",
"requirements": [
"python-roborock==3.8.4",
"python-roborock==3.9.2",
"vacuum-map-parser-roborock==0.1.4"
]
}

View File

@@ -1,6 +1,5 @@
"""Roborock storage."""
import dataclasses
import logging
from pathlib import Path
import shutil
@@ -17,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
STORAGE_PATH = f".storage/{DOMAIN}"
MAPS_PATH = "maps"
CACHE_VERSION = 1
CACHE_VERSION = 2
def _storage_path_prefix(hass: HomeAssistant, entry_id: str) -> Path:
@@ -44,6 +43,31 @@ async def async_cleanup_map_storage(hass: HomeAssistant, entry_id: str) -> None:
await hass.async_add_executor_job(remove, path_prefix)
class StoreImpl(Store[dict[str, Any]]):
"""Store implementation for Roborock cache."""
def __init__(self, hass: HomeAssistant, entry_id: str) -> None:
"""Initialize StoreImpl."""
super().__init__(
hass,
version=CACHE_VERSION,
key=f"{DOMAIN}/{entry_id}",
private=True,
)
async def _async_migrate_func(
self,
old_major_version: int,
old_minor_version: int,
old_data: dict[str, Any],
) -> dict[str, Any]:
"""Wipe out old caches with the old format."""
if old_major_version == 1:
# No need for migration as version 1 was never in any stable releases
return {}
return old_data
class CacheStore(Cache):
"""Store and retrieve cache for a Roborock device.
@@ -55,19 +79,14 @@ class CacheStore(Cache):
def __init__(self, hass: HomeAssistant, entry_id: str) -> None:
"""Initialize CacheStore."""
self._cache_store = Store[dict[str, Any]](
hass,
version=CACHE_VERSION,
key=f"{DOMAIN}/{entry_id}",
private=True,
)
self._cache_store = StoreImpl(hass, entry_id)
self._cache_data: CacheData | None = None
async def get(self) -> CacheData:
"""Retrieve cached metadata."""
if self._cache_data is None:
if data := await self._cache_store.async_load():
self._cache_data = CacheData(**data)
self._cache_data = CacheData.from_dict(data)
else:
self._cache_data = CacheData()
@@ -80,7 +99,7 @@ class CacheStore(Cache):
async def flush(self) -> None:
"""Flush cached metadata to disk."""
if self._cache_data is not None:
await self._cache_store.async_save(dataclasses.asdict(self._cache_data))
await self._cache_store.async_save(self._cache_data.as_dict())
async def async_remove(self) -> None:
"""Remove cached metadata from disk."""

View File

@@ -150,12 +150,9 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
@property
def available(self) -> bool:
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except KeyError:
return False
return super().available
return (
super().available and self._segment in self.coordinator.data.state.segments
)
@property
def rgb_color(self) -> tuple[int, int, int] | None:

View File

@@ -97,12 +97,9 @@ class WLEDNumber(WLEDEntity, NumberEntity):
@property
def available(self) -> bool:
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except KeyError:
return False
return super().available
return (
super().available and self._segment in self.coordinator.data.state.segments
)
@property
def native_value(self) -> float | None:

View File

@@ -31,10 +31,6 @@ rules:
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: todo
entity-unavailable:
status: todo
comment: |
The WLEDSegmentLight.available property can just be an if .. in .. check
integration-owner: done
log-when-unavailable: done
parallel-updates: done

View File

@@ -173,12 +173,9 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
@property
def available(self) -> bool:
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except KeyError:
return False
return super().available
return (
super().available and self._segment in self.coordinator.data.state.segments
)
@property
def current_option(self) -> str | None:

View File

@@ -167,12 +167,9 @@ class WLEDReverseSwitch(WLEDEntity, SwitchEntity):
@property
def available(self) -> bool:
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except KeyError:
return False
return super().available
return (
super().available and self._segment in self.coordinator.data.state.segments
)
@property
def is_on(self) -> bool:

View File

@@ -4511,7 +4511,7 @@
},
"nordpool": {
"name": "Nord Pool",
"integration_type": "hub",
"integration_type": "service",
"config_flow": true,
"iot_class": "cloud_polling",
"single_config_entry": true
@@ -4832,7 +4832,7 @@
},
"oralb": {
"name": "Oral-B",
"integration_type": "hub",
"integration_type": "device",
"config_flow": true,
"iot_class": "local_push"
},

View File

@@ -6,8 +6,8 @@ To update, run python3 -m script.hassfest
LABS_PREVIEW_FEATURES = {
"automation": {
"new_triggers_conditions": {
"feedback_url": "",
"learn_more_url": "",
"feedback_url": "https://forms.gle/fWFZqf5MzuwWTsCH8",
"learn_more_url": "https://www.home-assistant.io/blog/2025/12/03/release-202512/#purpose-specific-triggers-and-conditions",
"report_issue_url": "https://github.com/home-assistant/core/issues/new?template=bug_report.yml&integration_link=https://www.home-assistant.io/integrations/automation&integration_name=Automation",
},
},

View File

@@ -36,7 +36,7 @@ fnv-hash-fast==1.6.0
go2rtc-client==0.3.0
ha-ffmpeg==3.2.2
habluetooth==5.8.0
hass-nabucasa==1.6.2
hass-nabucasa==1.7.0
hassil==3.5.0
home-assistant-bluetooth==1.13.1
home-assistant-frontend==20251202.0

View File

@@ -48,7 +48,7 @@ dependencies = [
"fnv-hash-fast==1.6.0",
# hass-nabucasa is imported by helpers which don't depend on the cloud
# integration
"hass-nabucasa==1.6.2",
"hass-nabucasa==1.7.0",
# When bumping httpx, please check the version pins of
# httpcore, anyio, and h11 in gen_requirements_all
"httpx==0.28.1",

2
requirements.txt generated
View File

@@ -22,7 +22,7 @@ certifi>=2021.5.30
ciso8601==2.3.3
cronsim==2.7
fnv-hash-fast==1.6.0
hass-nabucasa==1.6.2
hass-nabucasa==1.7.0
httpx==0.28.1
home-assistant-bluetooth==1.13.1
ifaddr==0.2.0

12
requirements_all.txt generated
View File

@@ -340,7 +340,7 @@ aiontfy==0.6.1
aionut==4.3.4
# homeassistant.components.onkyo
aioonkyo==0.3.0
aioonkyo==0.4.0
# homeassistant.components.openexchangerates
aioopenexchangerates==0.6.8
@@ -1090,7 +1090,7 @@ google-genai==1.38.0
google-maps-routing==0.6.15
# homeassistant.components.nest
google-nest-sdm==9.1.1
google-nest-sdm==9.1.2
# homeassistant.components.google_photos
google-photos-library-api==0.12.1
@@ -1163,7 +1163,7 @@ habluetooth==5.8.0
hanna-cloud==0.0.6
# homeassistant.components.cloud
hass-nabucasa==1.6.2
hass-nabucasa==1.7.0
# homeassistant.components.splunk
hass-splunk==0.1.1
@@ -1288,7 +1288,7 @@ insteon-frontend-home-assistant==0.5.0
intellifire4py==4.2.1
# homeassistant.components.iometer
iometer==0.2.0
iometer==0.3.0
# homeassistant.components.iotty
iottycloud==0.3.0
@@ -2563,7 +2563,7 @@ python-rabbitair==0.0.8
python-ripple-api==0.0.3
# homeassistant.components.roborock
python-roborock==3.8.4
python-roborock==3.9.2
# homeassistant.components.smarttub
python-smarttub==0.0.45
@@ -3191,7 +3191,7 @@ wyoming==1.7.2
xiaomi-ble==1.2.0
# homeassistant.components.knx
xknx==3.11.0
xknx==3.12.0
# homeassistant.components.knx
xknxproject==3.8.2

View File

@@ -325,7 +325,7 @@ aiontfy==0.6.1
aionut==4.3.4
# homeassistant.components.onkyo
aioonkyo==0.3.0
aioonkyo==0.4.0
# homeassistant.components.openexchangerates
aioopenexchangerates==0.6.8
@@ -966,7 +966,7 @@ google-genai==1.38.0
google-maps-routing==0.6.15
# homeassistant.components.nest
google-nest-sdm==9.1.1
google-nest-sdm==9.1.2
# homeassistant.components.google_photos
google-photos-library-api==0.12.1
@@ -1033,7 +1033,7 @@ habluetooth==5.8.0
hanna-cloud==0.0.6
# homeassistant.components.cloud
hass-nabucasa==1.6.2
hass-nabucasa==1.7.0
# homeassistant.components.assist_satellite
# homeassistant.components.conversation
@@ -1134,7 +1134,7 @@ insteon-frontend-home-assistant==0.5.0
intellifire4py==4.2.1
# homeassistant.components.iometer
iometer==0.2.0
iometer==0.3.0
# homeassistant.components.iotty
iottycloud==0.3.0
@@ -2144,7 +2144,7 @@ python-pooldose==0.8.1
python-rabbitair==0.0.8
# homeassistant.components.roborock
python-roborock==3.8.4
python-roborock==3.9.2
# homeassistant.components.smarttub
python-smarttub==0.0.45
@@ -2658,7 +2658,7 @@ wyoming==1.7.2
xiaomi-ble==1.2.0
# homeassistant.components.knx
xknx==3.11.0
xknx==3.12.0
# homeassistant.components.knx
xknxproject==3.8.2

View File

@@ -205,8 +205,8 @@ async def test_ws_get_client_config(
"iceServers": [
{
"urls": [
"stun:stun.home-assistant.io:80",
"stun:stun.home-assistant.io:3478",
"stun:stun.home-assistant.io:80",
]
},
],
@@ -238,8 +238,8 @@ async def test_ws_get_client_config(
"iceServers": [
{
"urls": [
"stun:stun.home-assistant.io:80",
"stun:stun.home-assistant.io:3478",
"stun:stun.home-assistant.io:80",
]
},
{

View File

@@ -11,6 +11,7 @@ import pytest
import voluptuous as vol
from homeassistant.components import conversation
from homeassistant.components.cloud.const import AI_TASK_ENTITY_UNIQUE_ID, DOMAIN
from homeassistant.components.cloud.entity import (
BaseCloudLLMEntity,
_convert_content_to_param,
@@ -18,7 +19,8 @@ from homeassistant.components.cloud.entity import (
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import llm, selector
from homeassistant.helpers import entity_registry as er, llm, selector
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
@@ -219,3 +221,66 @@ async def test_prepare_chat_for_generation_passes_messages_through(
assert response["messages"] == messages
assert response["conversation_id"] == "conversation-id"
async def test_async_handle_chat_log_service_sets_structured_output_non_strict(
hass: HomeAssistant,
cloud: MagicMock,
entity_registry: er.EntityRegistry,
mock_cloud_login: None,
) -> None:
"""Ensure structured output requests always disable strict validation via service."""
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
on_start_callback = cloud.register_on_start.call_args[0][0]
await on_start_callback()
await hass.async_block_till_done()
entity_id = entity_registry.async_get_entity_id(
"ai_task", DOMAIN, AI_TASK_ENTITY_UNIQUE_ID
)
assert entity_id is not None
async def _empty_stream():
return
async def _fake_delta_stream(
self: conversation.ChatLog,
agent_id: str,
stream,
):
content = conversation.AssistantContent(
agent_id=agent_id, content='{"value": "ok"}'
)
self.async_add_assistant_content_without_tools(content)
yield content
cloud.llm.async_generate_data = AsyncMock(return_value=_empty_stream())
with patch(
"homeassistant.components.conversation.chat_log.ChatLog.async_add_delta_content_stream",
_fake_delta_stream,
):
await hass.services.async_call(
"ai_task",
"generate_data",
{
"entity_id": entity_id,
"task_name": "Device Report",
"instructions": "Provide value.",
"structure": {
"value": {
"selector": {"text": None},
"required": True,
}
},
},
blocking=True,
return_response=True,
)
cloud.llm.async_generate_data.assert_awaited_once()
_, kwargs = cloud.llm.async_generate_data.call_args
assert kwargs["response_format"]["json_schema"]["strict"] is False

View File

@@ -36,6 +36,7 @@ async def test_diagnostic_entities(
"sensor.knx_interface_outgoing_telegrams",
"sensor.knx_interface_outgoing_telegram_errors",
"sensor.knx_interface_telegrams",
"sensor.knx_interface_undecodable_data_secure_telegrams",
):
entity = entity_registry.async_get(entity_id)
assert entity.entity_category is EntityCategory.DIAGNOSTIC
@@ -43,6 +44,7 @@ async def test_diagnostic_entities(
for entity_id in (
"sensor.knx_interface_incoming_telegrams",
"sensor.knx_interface_outgoing_telegrams",
"sensor.knx_interface_undecodable_data_secure_telegrams",
):
entity = entity_registry.async_get(entity_id)
assert entity.disabled is True
@@ -57,7 +59,7 @@ async def test_diagnostic_entities(
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(events) == 3 # 5 polled sensors - 2 disabled
assert len(events) == 3 # 6 polled sensors - 3 disabled
events.clear()
for entity_id, test_state in (
@@ -74,7 +76,7 @@ async def test_diagnostic_entities(
state=XknxConnectionState.DISCONNECTED
)
await hass.async_block_till_done()
assert len(events) == 4 # 3 not always_available + 3 force_update - 2 disabled
assert len(events) == 4
events.clear()
knx.xknx.current_address = IndividualAddress("1.1.1")

View File

@@ -22,7 +22,7 @@ class MockAsyncSubprocess:
"""Fails immediately with a timeout."""
raise TimeoutError
async def kill(self) -> None:
def kill(self) -> None:
"""Raise preset exception when called."""
raise self.killsig