Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -18,7 +18,7 @@ repos:
exclude_types: [csv, json, html]
exclude: ^tests/fixtures/|homeassistant/generated/|tests/components/.*/snapshots/
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: check-executables-have-shebangs
stages: [manual]
Expand Down
10 changes: 5 additions & 5 deletions homeassistant/components/assist_satellite/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Any, Literal, final

from hassil import Intents, recognize
from hassil.expression import Expression, ListReference, Sequence
from hassil.expression import Expression, Group, ListReference
from hassil.intents import WildcardSlotList

from homeassistant.components import conversation, media_source, stt, tts
Expand Down Expand Up @@ -413,7 +413,7 @@ def _question_response_to_answer(
for intent in intents.intents.values():
for intent_data in intent.data:
for sentence in intent_data.sentences:
_collect_list_references(sentence, wildcard_names)
_collect_list_references(sentence.expression, wildcard_names)

for wildcard_name in wildcard_names:
intents.slot_lists[wildcard_name] = WildcardSlotList(wildcard_name)
Expand Down Expand Up @@ -727,9 +727,9 @@ async def _resolve_announcement_media_id(

def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
"""Collect list reference names recursively."""
if isinstance(expression, Sequence):
seq: Sequence = expression
for item in seq.items:
if isinstance(expression, Group):
grp: Group = expression
for item in grp.items:
_collect_list_references(item, list_names)
elif isinstance(expression, ListReference):
# {list}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/assist_satellite/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/assist_satellite",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["hassil==2.2.3"]
"requirements": ["hassil==3.1.0"]
}
10 changes: 5 additions & 5 deletions homeassistant/components/conversation/default_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import time
from typing import IO, Any, cast

from hassil.expression import Expression, ListReference, Sequence, TextChunk
from hassil.expression import Expression, Group, ListReference, TextChunk
from hassil.intents import (
Intents,
SlotList,
Expand Down Expand Up @@ -1183,7 +1183,7 @@ def _rebuild_trigger_intents(self) -> None:
for trigger_intent in trigger_intents.intents.values():
for intent_data in trigger_intent.data:
for sentence in intent_data.sentences:
_collect_list_references(sentence, wildcard_names)
_collect_list_references(sentence.expression, wildcard_names)

for wildcard_name in wildcard_names:
trigger_intents.slot_lists[wildcard_name] = WildcardSlotList(wildcard_name)
Expand Down Expand Up @@ -1520,9 +1520,9 @@ def _get_match_error_response(

def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
"""Collect list reference names recursively."""
if isinstance(expression, Sequence):
seq: Sequence = expression
for item in seq.items:
if isinstance(expression, Group):
grp: Group = expression
for item in grp.items:
_collect_list_references(item, list_names)
elif isinstance(expression, ListReference):
# {list}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/conversation/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==2.2.3", "home-assistant-intents==2025.7.30"]
"requirements": ["hassil==3.1.0", "home-assistant-intents==2025.7.30"]
}
31 changes: 31 additions & 0 deletions homeassistant/components/fan/intent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Intents for the fan integration."""

import voluptuous as vol

from homeassistant.core import HomeAssistant
from homeassistant.helpers import intent

from . import ATTR_PERCENTAGE, DOMAIN, SERVICE_TURN_ON

INTENT_FAN_SET_SPEED = "HassFanSetSpeed"


async def async_setup_intents(hass: HomeAssistant) -> None:
"""Set up the fan intents."""
intent.async_register(
hass,
intent.ServiceIntentHandler(
INTENT_FAN_SET_SPEED,
DOMAIN,
SERVICE_TURN_ON,
description="Sets a fan's speed by percentage",
required_domains={DOMAIN},
platforms={DOMAIN},
required_slots={
ATTR_PERCENTAGE: intent.IntentSlotInfo(
description="The speed percentage of the fan",
value_schema=vol.All(vol.Coerce(int), vol.Range(min=0, max=100)),
)
},
),
)
7 changes: 7 additions & 0 deletions homeassistant/components/pi_hole/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ async def determine_api_version(
_LOGGER.debug(
"Connection to %s failed: %s, trying API version 5", holeV6.base_url, ex_v6
)
else:
# It seems that occasionally the auth can succeed unexpectedly when there is a valid session
_LOGGER.warning(
"Authenticated with %s through v6 API, but succeeded with an incorrect password. This is a known bug",
holeV6.base_url,
)
return 6
holeV5 = api_by_version(hass, entry, 5, password="wrong_token")
try:
await holeV5.get_data()
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/package_constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ go2rtc-client==0.2.1
ha-ffmpeg==3.2.2
habluetooth==5.0.1
hass-nabucasa==0.111.2
hassil==2.2.3
hassil==3.1.0
home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250811.0
home-assistant-intents==2025.7.30
Expand All @@ -45,7 +45,7 @@ ifaddr==0.2.0
Jinja2==3.1.6
lru-dict==1.3.0
mutagen==1.47.0
orjson==3.11.1
orjson==3.11.2
packaging>=23.1
paho-mqtt==2.1.0
Pillow==11.3.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dependencies = [
"Pillow==11.3.0",
"propcache==0.3.2",
"pyOpenSSL==25.1.0",
"orjson==3.11.1",
"orjson==3.11.2",
"packaging>=23.1",
"psutil-home-assistant==0.0.1",
"python-slugify==8.0.4",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt

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

2 changes: 1 addition & 1 deletion requirements_all.txt

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

20 changes: 10 additions & 10 deletions requirements_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ requests-mock==1.12.1
respx==0.22.0
syrupy==4.9.1
tqdm==4.67.1
types-aiofiles==24.1.0.20250708
types-aiofiles==24.1.0.20250809
types-atomicwrites==1.4.5.1
types-croniter==6.0.0.20250626
types-croniter==6.0.0.20250809
types-caldav==1.3.0.20250516
types-chardet==0.1.5
types-decorator==5.2.0.20250324
types-pexpect==4.9.0.20250516
types-protobuf==6.30.2.20250703
types-psutil==7.0.0.20250601
types-pyserial==3.5.0.20250326
types-python-dateutil==2.9.0.20250708
types-pexpect==4.9.0.20250809
types-protobuf==6.30.2.20250809
types-psutil==7.0.0.20250801
types-pyserial==3.5.0.20250809
types-python-dateutil==2.9.0.20250809
types-python-slugify==8.0.2.20240310
types-pytz==2025.2.0.20250516
types-PyYAML==6.0.12.20250516
types-requests==2.32.4.20250611
types-pytz==2025.2.0.20250809
types-PyYAML==6.0.12.20250809
types-requests==2.32.4.20250809
types-xmltodict==0.13.0.3
2 changes: 1 addition & 1 deletion requirements_test_all.txt

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

2 changes: 1 addition & 1 deletion script/hassfest/docker/Dockerfile

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

2 changes: 1 addition & 1 deletion tests/components/asuswrt/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def _set_fail_types(fail_types):
def mock_controller_connect_http_sens_detect():
"""Mock a successful sensor detection using http library."""

def _get_sensors_side_effect(datatype):
async def _get_sensors_side_effect(datatype):
if datatype == AsusData.TEMPERATURE:
return list(MOCK_TEMPERATURES_HTTP)
if datatype == AsusData.CPU:
Expand Down
1 change: 0 additions & 1 deletion tests/components/asuswrt/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,6 @@ async def test_cpu_sensors_http(
connect_http_sens_detect,
) -> None:
"""Test creating AsusWRT cpu sensors."""
connect_http_sens_detect(AsusData.CPU)
config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA_HTTP, SENSORS_CPU)
config_entry.add_to_hass(hass)

Expand Down
37 changes: 37 additions & 0 deletions tests/components/fan/test_intent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Intent tests for the fan platform."""

from homeassistant.components.fan import (
ATTR_PERCENTAGE,
DOMAIN,
SERVICE_TURN_ON,
intent as fan_intent,
)
from homeassistant.const import STATE_OFF
from homeassistant.core import HomeAssistant
from homeassistant.helpers import intent

from tests.common import async_mock_service


async def test_set_speed_intent(hass: HomeAssistant) -> None:
"""Test set speed intent for fans."""
await fan_intent.async_setup_intents(hass)

entity_id = f"{DOMAIN}.test_fan"
hass.states.async_set(entity_id, STATE_OFF)
calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)

response = await intent.async_handle(
hass,
"test",
fan_intent.INTENT_FAN_SET_SPEED,
{"name": {"value": "test fan"}, ATTR_PERCENTAGE: {"value": 50}},
)
await hass.async_block_till_done()

assert response.response_type == intent.IntentResponseType.ACTION_DONE
assert len(calls) == 1
call = calls[0]
assert call.domain == DOMAIN
assert call.service == SERVICE_TURN_ON
assert call.data == {"entity_id": entity_id, "percentage": 50}
6 changes: 5 additions & 1 deletion tests/components/pi_hole/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,16 @@ async def authenticate_side_effect(*_args, **_kwargs):
if wrong_host:
raise HoleConnectionError("Cannot authenticate with Pi-hole: err")
password = getattr(mocked_hole, "password", None)

if (
raise_exception
or incorrect_app_password
or api_version == 5
or (api_version == 6 and password not in ["newkey", "apikey"])
):
if api_version == 6:
if api_version == 6 and (
incorrect_app_password or password not in ["newkey", "apikey"]
):
raise HoleError("Authentication failed: Invalid password")
raise HoleConnectionError

Expand Down
1 change: 1 addition & 0 deletions tests/components/squeezebox/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def mock_pysqueezebox_player(uuid: str) -> MagicMock:
"homeassistant.components.squeezebox.Player", autospec=True
) as mock_player:
mock_player.async_browse = AsyncMock(side_effect=mock_async_browse)
mock_player.async_query = AsyncMock(return_value=MagicMock())
mock_player.generate_image_url_from_track_id = MagicMock(
return_value="http://lms.internal:9000/html/images/favorites.png"
)
Expand Down
Loading