mirror of
https://github.com/home-assistant/core.git
synced 2026-06-12 20:21:40 +02:00
Compare commits
9 Commits
add-naive-now
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 96c5774bef | |||
| 6288905ca5 | |||
| 4c1dbec599 | |||
| 2a0b5ca895 | |||
| 746c8dd908 | |||
| dadfea4d62 | |||
| 53aef99921 | |||
| 88bd563a2c | |||
| c57c8fad16 |
@@ -7,6 +7,6 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pydroplet==2.3.4"],
|
||||
"requirements": ["pydroplet==2.4.0"],
|
||||
"zeroconf": ["_droplet._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import time
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from urllib.parse import urlparse
|
||||
import uuid
|
||||
import warnings
|
||||
|
||||
from aiohttp import ClientError, ClientResponse, ClientSession, web
|
||||
from aiohttp.hdrs import AUTHORIZATION
|
||||
import jwt
|
||||
from jwt.warnings import InsecureKeyLengthWarning
|
||||
from py_vapid import Vapid
|
||||
from pywebpush import WebPusher, WebPushException, webpush_async
|
||||
import voluptuous as vol
|
||||
@@ -325,7 +327,8 @@ class HTML5PushCallbackView(HomeAssistantView):
|
||||
if target_check.get(ATTR_TARGET) in self.registrations:
|
||||
possible_target = self.registrations[target_check[ATTR_TARGET]]
|
||||
key = possible_target["subscription"]["keys"]["auth"]
|
||||
with suppress(jwt.exceptions.DecodeError):
|
||||
with suppress(jwt.exceptions.DecodeError), warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", InsecureKeyLengthWarning)
|
||||
return jwt.decode(token, key, algorithms=["ES256", "HS256"])
|
||||
|
||||
return self.json_message(
|
||||
@@ -585,7 +588,9 @@ def add_jwt(timestamp: int, target: str, tag: str, jwt_secret: str) -> str:
|
||||
ATTR_TARGET: target,
|
||||
ATTR_TAG: tag,
|
||||
}
|
||||
return jwt.encode(jwt_claims, jwt_secret)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", InsecureKeyLengthWarning)
|
||||
return jwt.encode(jwt_claims, jwt_secret)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
||||
@@ -248,7 +248,10 @@ def _generate_thumbnail_if_file_does_not_exist(
|
||||
if not target_file.is_file():
|
||||
image = ImageOps.exif_transpose(Image.open(original_path))
|
||||
image.thumbnail(target_size)
|
||||
image.save(target_path, format=content_type.partition("/")[-1])
|
||||
save_format = content_type.partition("/")[-1]
|
||||
if save_format == "jpeg" and image.mode not in ("RGB", "L", "CMYK"):
|
||||
image = image.convert("RGB")
|
||||
image.save(target_path, format=save_format)
|
||||
|
||||
|
||||
def _validate_size_from_filename(filename: str) -> tuple[int, int]:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Support for LG TV running on NetCast 3 or 4."""
|
||||
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
@@ -133,13 +134,22 @@ class LgTVDevice(MediaPlayerEntity):
|
||||
|
||||
channel_list = client.query_data("channel_list")
|
||||
if channel_list:
|
||||
channel_names = []
|
||||
channel_pairs = []
|
||||
for channel in channel_list:
|
||||
channel_name = channel.find("chname")
|
||||
if channel_name is not None:
|
||||
channel_names.append(str(channel_name.text))
|
||||
self._sources = dict(zip(channel_names, channel_list, strict=False))
|
||||
# sort source names by the major channel number
|
||||
channel_pairs.append((str(channel_name.text), channel))
|
||||
|
||||
name_count = Counter(name for name, _ in channel_pairs)
|
||||
|
||||
self._sources = {}
|
||||
for name, channel in channel_pairs:
|
||||
if name_count[name] > 1:
|
||||
major = channel.find("major")
|
||||
if major is not None:
|
||||
name = f"{name} ({major.text})"
|
||||
self._sources[name] = channel
|
||||
|
||||
source_tuples = [
|
||||
(k, source.find("major").text)
|
||||
for k, source in self._sources.items()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["boto3", "botocore", "pyoverkiz", "s3transfer"],
|
||||
"requirements": ["pyoverkiz[nexity]==2.0.0"],
|
||||
"requirements": ["pyoverkiz[nexity]==2.0.1"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"name": "gateway*",
|
||||
|
||||
@@ -31,7 +31,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: YaleConfigEntry) -> boo
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: YaleConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
LOGGER.debug("Migrating from version %s", entry.version)
|
||||
LOGGER.debug("Migrating from version %s.%s", entry.version, entry.minor_version)
|
||||
|
||||
if entry.version == 1:
|
||||
new_options = entry.options.copy()
|
||||
@@ -55,6 +55,19 @@ async def async_migrate_entry(hass: HomeAssistant, entry: YaleConfigEntry) -> bo
|
||||
del new_data[CONF_NAME]
|
||||
hass.config_entries.async_update_entry(entry, data=new_data, minor_version=2)
|
||||
|
||||
LOGGER.debug("Migration to version %s successful", entry.version)
|
||||
if entry.version == 2 and entry.minor_version == 2:
|
||||
entity_reg = er.async_get(hass)
|
||||
entries = er.async_entries_for_config_entry(entity_reg, entry.entry_id)
|
||||
for entity in entries:
|
||||
if entity.unique_id == "yale_smart_alarm-panic":
|
||||
entity_reg.async_update_entity(
|
||||
entity.entity_id,
|
||||
new_unique_id=f"{entry.entry_id}-panic",
|
||||
)
|
||||
hass.config_entries.async_update_entry(entry, minor_version=3)
|
||||
|
||||
LOGGER.debug(
|
||||
"Migration to version %s.%s successful", entry.version, entry.minor_version
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -47,7 +47,7 @@ class YalePanicButton(YaleAlarmEntity, ButtonEntity):
|
||||
"""Initialize the plug switch."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"yale_smart_alarm-{description.key}"
|
||||
self._attr_unique_id = f"{coordinator.config_entry.entry_id}-{description.key}"
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Press the button."""
|
||||
|
||||
@@ -64,7 +64,7 @@ class YaleConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Yale integration."""
|
||||
|
||||
VERSION = 2
|
||||
MINOR_VERSION = 2
|
||||
MINOR_VERSION = 3
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["yoto_api"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["yoto-api==4.1.0"]
|
||||
"requirements": ["yoto-api==4.2.0"]
|
||||
}
|
||||
|
||||
@@ -700,7 +700,7 @@ def _get_exposed_entities(
|
||||
):
|
||||
# Entity is in area
|
||||
area_names.append(area_entry.name)
|
||||
area_names.extend(area_entry.aliases)
|
||||
area_names.extend(sorted(area_entry.aliases))
|
||||
elif device_entry is not None:
|
||||
# Check device area
|
||||
if (
|
||||
@@ -711,7 +711,7 @@ def _get_exposed_entities(
|
||||
is not None
|
||||
):
|
||||
area_names.append(area_entry.name)
|
||||
area_names.extend(area_entry.aliases)
|
||||
area_names.extend(sorted(area_entry.aliases))
|
||||
|
||||
info: dict[str, Any] = {
|
||||
"names": ", ".join(names),
|
||||
@@ -962,9 +962,9 @@ def _get_cached_action_parameters(
|
||||
aliases = er.async_get_entity_aliases(hass, entity_entry)
|
||||
if aliases:
|
||||
if description:
|
||||
description = description + ". Aliases: " + str(list(aliases))
|
||||
description = description + ". Aliases: " + str(sorted(aliases))
|
||||
else:
|
||||
description = "Aliases: " + str(list(aliases))
|
||||
description = "Aliases: " + str(sorted(aliases))
|
||||
|
||||
parameters_cache.setdefault(domain, {})[action] = (description, parameters)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Config entry functions for Home Assistant templates."""
|
||||
|
||||
from collections.abc import Iterable
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from homeassistant.exceptions import TemplateError
|
||||
@@ -104,4 +105,6 @@ class ConfigEntryExtension(BaseTemplateExtension):
|
||||
if config_entry is None:
|
||||
return None
|
||||
|
||||
return getattr(config_entry, attr_name)
|
||||
if isinstance(result := getattr(config_entry, attr_name), Enum):
|
||||
return result.value
|
||||
return result
|
||||
|
||||
Generated
+3
-3
@@ -2120,7 +2120,7 @@ pydrawise==2026.4.0
|
||||
pydroid-ipcam==3.0.0
|
||||
|
||||
# homeassistant.components.droplet
|
||||
pydroplet==2.3.4
|
||||
pydroplet==2.4.0
|
||||
|
||||
# homeassistant.components.ebox
|
||||
pyebox==1.1.4
|
||||
@@ -2438,7 +2438,7 @@ pyotgw==2.2.3
|
||||
pyotp==2.9.0
|
||||
|
||||
# homeassistant.components.overkiz
|
||||
pyoverkiz[nexity]==2.0.0
|
||||
pyoverkiz[nexity]==2.0.1
|
||||
|
||||
# homeassistant.components.palazzetti
|
||||
pypalazzetti==0.1.20
|
||||
@@ -3436,7 +3436,7 @@ yeelightsunflower==0.0.10
|
||||
yolink-api==0.6.5
|
||||
|
||||
# homeassistant.components.yoto
|
||||
yoto-api==4.1.0
|
||||
yoto-api==4.2.0
|
||||
|
||||
# homeassistant.components.youless
|
||||
youless-api==2.2.0
|
||||
|
||||
@@ -5,9 +5,11 @@ from http import HTTPStatus
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, mock_open, patch
|
||||
import warnings
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp.hdrs import AUTHORIZATION
|
||||
import jwt.warnings
|
||||
import pytest
|
||||
from pywebpush import WebPushException
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
@@ -1289,3 +1291,11 @@ async def test_html5_dismiss_message(
|
||||
"data": {"jwt": "JWT"},
|
||||
**expected_payload,
|
||||
}
|
||||
|
||||
|
||||
def test_add_jwt_no_insecure_key_warning() -> None:
|
||||
"""Test that add_jwt does not emit InsecureKeyLengthWarning for short keys."""
|
||||
short_key = "c2hvcnRfa2V5X2hlcmU="
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", jwt.warnings.InsecureKeyLengthWarning)
|
||||
html5.add_jwt(1234567890, "device", "tag", short_key)
|
||||
|
||||
@@ -6,6 +6,8 @@ from unittest.mock import patch
|
||||
|
||||
from aiohttp import ClientSession, ClientWebSocketResponse
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from PIL import Image
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.image_upload import DOMAIN
|
||||
from homeassistant.components.websocket_api import TYPE_RESULT
|
||||
@@ -94,3 +96,57 @@ async def test_upload_image(
|
||||
|
||||
# Ensure removed from disk
|
||||
assert not item_folder.is_dir()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("image_mode", "content_type"),
|
||||
[
|
||||
("RGBA", "image/jpeg"),
|
||||
("LA", "image/jpeg"),
|
||||
("P", "image/jpeg"),
|
||||
],
|
||||
)
|
||||
async def test_upload_image_thumbnail_rgba_as_jpeg(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
hass_client: ClientSessionGenerator,
|
||||
image_mode: str,
|
||||
content_type: str,
|
||||
) -> None:
|
||||
"""Test thumbnail generation when image mode is incompatible with JPEG."""
|
||||
now = dt_util.utcnow()
|
||||
freezer.move_to(now)
|
||||
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tempdir,
|
||||
patch.object(hass.config, "path", return_value=tempdir),
|
||||
):
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
client: ClientSession = await hass_client()
|
||||
|
||||
with TEST_IMAGE.open("rb") as fp:
|
||||
res = await client.post("/api/image/upload", data={"file": fp})
|
||||
|
||||
assert res.status == 200
|
||||
item = await res.json()
|
||||
image_id = item["id"]
|
||||
|
||||
tempdir = pathlib.Path(tempdir)
|
||||
item_folder = tempdir / image_id
|
||||
|
||||
# Create an image file with the given mode to simulate the mismatch
|
||||
original_path = item_folder / "original"
|
||||
img = Image.new(image_mode, (300, 300))
|
||||
img.save(original_path, format="png")
|
||||
|
||||
# Change the stored content_type to simulate the mismatch
|
||||
hass.data[DOMAIN].data[image_id]["content_type"] = content_type
|
||||
|
||||
# Fetch the thumbnail; this should not raise an OSError
|
||||
res = await client.get(f"/api/image/serve/{image_id}/256x256")
|
||||
assert res.status == 200
|
||||
assert (item_folder / "256x256").is_file()
|
||||
|
||||
# Verify the generated thumbnail is a valid JPEG
|
||||
thumbnail = Image.open(item_folder / "256x256")
|
||||
assert thumbnail.mode == "RGB"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Tests for LG Netcast media player platform."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, patch
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import MODEL_NAME, setup_lgnetcast
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
ENTITY_ID = f"{MP_DOMAIN}.{MODEL_NAME.lower()}"
|
||||
|
||||
|
||||
def _make_channel(name: str, major: str) -> ET.Element:
|
||||
"""Create a fake channel XML element."""
|
||||
channel = ET.Element("data")
|
||||
chname = ET.SubElement(channel, "chname")
|
||||
chname.text = name
|
||||
major_el = ET.SubElement(channel, "major")
|
||||
major_el.text = major
|
||||
return channel
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_lg_netcast() -> Generator[MagicMock]:
|
||||
"""Mock LG Netcast library."""
|
||||
with patch(
|
||||
"homeassistant.components.lg_netcast.LgNetCastClient"
|
||||
) as mock_client_class:
|
||||
yield mock_client_class
|
||||
|
||||
|
||||
async def test_source_list_duplicate_channel_names(
|
||||
hass: HomeAssistant,
|
||||
mock_lg_netcast: MagicMock,
|
||||
) -> None:
|
||||
"""Test that duplicate channel names are disambiguated in source list."""
|
||||
client = mock_lg_netcast.return_value
|
||||
client.get_volume.return_value = (20, False)
|
||||
context_client = client.__enter__.return_value
|
||||
channel_data = {
|
||||
"cur_channel": None,
|
||||
"channel_list": [
|
||||
_make_channel("BBC One", "1"),
|
||||
_make_channel("ITV", "3"),
|
||||
_make_channel("BBC One", "101"),
|
||||
],
|
||||
}
|
||||
context_client.query_data.side_effect = channel_data.get
|
||||
|
||||
await setup_lgnetcast(hass)
|
||||
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=60))
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
entity = hass.states.get(ENTITY_ID)
|
||||
assert entity is not None
|
||||
source_list = entity.attributes.get("source_list")
|
||||
assert source_list is not None
|
||||
assert len(source_list) == 3
|
||||
assert "ITV" in source_list
|
||||
assert "BBC One (1)" in source_list
|
||||
assert "BBC One (101)" in source_list
|
||||
@@ -46,7 +46,7 @@ async def load_config_entry(
|
||||
entry_id="1",
|
||||
unique_id="username",
|
||||
version=2,
|
||||
minor_version=2,
|
||||
minor_version=3,
|
||||
)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'panic',
|
||||
'unique_id': 'yale_smart_alarm-panic',
|
||||
'unique_id': '1-panic',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -27,7 +27,7 @@ async def test_setup_entry(
|
||||
entry_id="1",
|
||||
unique_id="username",
|
||||
version=2,
|
||||
minor_version=2,
|
||||
minor_version=3,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
@@ -87,7 +87,7 @@ async def test_migrate_entry(
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert entry.version == 2
|
||||
assert entry.minor_version == 2
|
||||
assert entry.minor_version == 3
|
||||
assert entry.data == ENTRY_CONFIG
|
||||
assert entry.options == OPTIONS_CONFIG
|
||||
|
||||
@@ -95,3 +95,47 @@ async def test_migrate_entry(
|
||||
lock = entity_registry.async_get(lock_entity_id)
|
||||
|
||||
assert lock.options == {"lock": {"default_code": "123456"}}
|
||||
|
||||
|
||||
async def test_migrate_panic_button_unique_id(
|
||||
hass: HomeAssistant,
|
||||
get_client: Mock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test migration of panic button unique_id from v2.2 to v2.3."""
|
||||
entry = MockConfigEntry(
|
||||
title=ENTRY_CONFIG["username"],
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data=ENTRY_CONFIG,
|
||||
options=OPTIONS_CONFIG,
|
||||
entry_id="1",
|
||||
unique_id="username",
|
||||
version=2,
|
||||
minor_version=2,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
entity_registry.async_get_or_create(
|
||||
"button",
|
||||
DOMAIN,
|
||||
"yale_smart_alarm-panic",
|
||||
config_entry=entry,
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.yale_smart_alarm.coordinator.YaleSmartAlarmClient",
|
||||
return_value=get_client,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert entry.version == 2
|
||||
assert entry.minor_version == 3
|
||||
|
||||
migrated = entity_registry.async_get_entity_id("button", DOMAIN, "1-panic")
|
||||
assert migrated is not None
|
||||
old = entity_registry.async_get_entity_id(
|
||||
"button", DOMAIN, "yale_smart_alarm-panic"
|
||||
)
|
||||
assert old is None
|
||||
|
||||
@@ -110,24 +110,37 @@ async def test_config_entry_id(
|
||||
|
||||
async def test_config_entry_attr(hass: HomeAssistant) -> None:
|
||||
"""Test config entry attr."""
|
||||
info = {
|
||||
config_entry = MockConfigEntry(
|
||||
domain="mock_light",
|
||||
title="mock title",
|
||||
source=config_entries.SOURCE_BLUETOOTH,
|
||||
disabled_by=config_entries.ConfigEntryDisabler.USER,
|
||||
pref_disable_polling=True,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
expected = {
|
||||
"domain": "mock_light",
|
||||
"title": "mock title",
|
||||
"source": config_entries.SOURCE_BLUETOOTH,
|
||||
"disabled_by": config_entries.ConfigEntryDisabler.USER,
|
||||
"pref_disable_polling": True,
|
||||
"disabled_by": "user",
|
||||
"pref_disable_polling": "True",
|
||||
"state": "not_loaded",
|
||||
}
|
||||
config_entry = MockConfigEntry(**info)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
info["state"] = config_entries.ConfigEntryState.NOT_LOADED
|
||||
|
||||
for key, value in info.items():
|
||||
assert render(
|
||||
hass,
|
||||
"{{ config_entry_attr('" + config_entry.entry_id + "', '" + key + "') }}",
|
||||
parse_result=False,
|
||||
) == str(value)
|
||||
for key, value in expected.items():
|
||||
assert (
|
||||
render(
|
||||
hass,
|
||||
"{{ config_entry_attr('"
|
||||
+ config_entry.entry_id
|
||||
+ "', '"
|
||||
+ key
|
||||
+ "') }}",
|
||||
parse_result=False,
|
||||
)
|
||||
== value
|
||||
)
|
||||
|
||||
for config_entry_id, key in (
|
||||
(config_entry.entry_id, "invalid_key"),
|
||||
|
||||
@@ -1234,7 +1234,7 @@ async def test_script_tool(
|
||||
assert tool.name == "test_script"
|
||||
assert (
|
||||
tool.description
|
||||
== "This is a test script. Aliases: ['script name', 'script alias']"
|
||||
== "This is a test script. Aliases: ['script alias', 'script name']"
|
||||
)
|
||||
schema = {
|
||||
vol.Required("beer", description="Number of beers"): cv.string,
|
||||
@@ -1249,7 +1249,7 @@ async def test_script_tool(
|
||||
|
||||
assert hass.data[llm.ACTION_PARAMETERS_CACHE]["script"] == {
|
||||
"test_script": (
|
||||
"This is a test script. Aliases: ['script name', 'script alias']",
|
||||
"This is a test script. Aliases: ['script alias', 'script name']",
|
||||
vol.Schema(schema),
|
||||
),
|
||||
"script_with_no_fields": (
|
||||
@@ -1358,14 +1358,14 @@ async def test_script_tool(
|
||||
assert tool.name == "test_script"
|
||||
assert (
|
||||
tool.description
|
||||
== "This is a new test script. Aliases: ['script name', 'script alias']"
|
||||
== "This is a new test script. Aliases: ['script alias', 'script name']"
|
||||
)
|
||||
schema = {vol.Required("beer", description="Number of beers"): cv.string}
|
||||
assert tool.parameters.schema == schema
|
||||
|
||||
assert hass.data[llm.ACTION_PARAMETERS_CACHE]["script"] == {
|
||||
"test_script": (
|
||||
"This is a new test script. Aliases: ['script name', 'script alias']",
|
||||
"This is a new test script. Aliases: ['script alias', 'script name']",
|
||||
vol.Schema(schema),
|
||||
),
|
||||
"script_with_no_fields": (
|
||||
|
||||
Reference in New Issue
Block a user