mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
2024.7.4 (#122770)
This commit is contained in:
commit
17930a6d66
@ -60,6 +60,7 @@ AUTH_EXCEPTIONS = (
|
||||
exceptions.NoCredentialsError,
|
||||
)
|
||||
CONNECTION_TIMEOUT_EXCEPTIONS = (
|
||||
OSError,
|
||||
asyncio.CancelledError,
|
||||
TimeoutError,
|
||||
exceptions.ConnectionLostError,
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.1"]
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==8.2.0"]
|
||||
}
|
||||
|
@ -73,6 +73,14 @@ SUPPORTED_SCHEMA_KEYS = {
|
||||
|
||||
def _format_schema(schema: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Format the schema to protobuf."""
|
||||
if (subschemas := schema.get("anyOf")) or (subschemas := schema.get("allOf")):
|
||||
for subschema in subschemas: # Gemini API does not support anyOf and allOf keys
|
||||
if "type" in subschema: # Fallback to first subschema with 'type' field
|
||||
return _format_schema(subschema)
|
||||
return _format_schema(
|
||||
subschemas[0]
|
||||
) # Or, if not found, to any of the subschemas
|
||||
|
||||
result = {}
|
||||
for key, val in schema.items():
|
||||
if key not in SUPPORTED_SCHEMA_KEYS:
|
||||
@ -81,12 +89,22 @@ def _format_schema(schema: dict[str, Any]) -> dict[str, Any]:
|
||||
key = "type_"
|
||||
val = val.upper()
|
||||
elif key == "format":
|
||||
if (schema.get("type") == "string" and val != "enum") or (
|
||||
schema.get("type") not in ("number", "integer", "string")
|
||||
):
|
||||
continue
|
||||
key = "format_"
|
||||
elif key == "items":
|
||||
val = _format_schema(val)
|
||||
elif key == "properties":
|
||||
val = {k: _format_schema(v) for k, v in val.items()}
|
||||
result[key] = val
|
||||
|
||||
if result.get("type_") == "OBJECT" and not result.get("properties"):
|
||||
# An object with undefined properties is not supported by Gemini API.
|
||||
# Fallback to JSON string. This will probably fail for most tools that want it,
|
||||
# but we don't have a better fallback strategy so far.
|
||||
result["properties"] = {"json": {"type_": "STRING"}}
|
||||
return result
|
||||
|
||||
|
||||
|
@ -11,6 +11,6 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohue"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aiohue==4.7.1"],
|
||||
"requirements": ["aiohue==4.7.2"],
|
||||
"zeroconf": ["_hue._tcp.local."]
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ async def async_setup_hue_events(bridge: HueBridge):
|
||||
CONF_ID: slugify(f"{hue_device.metadata.name} Button"),
|
||||
CONF_DEVICE_ID: device.id, # type: ignore[union-attr]
|
||||
CONF_UNIQUE_ID: hue_resource.id,
|
||||
CONF_TYPE: hue_resource.button.last_event.value,
|
||||
CONF_TYPE: hue_resource.button.button_report.event.value,
|
||||
CONF_SUBTYPE: hue_resource.metadata.control_id,
|
||||
}
|
||||
hass.bus.async_fire(ATTR_HUE_EVENT, data)
|
||||
@ -79,7 +79,7 @@ async def async_setup_hue_events(bridge: HueBridge):
|
||||
data = {
|
||||
CONF_DEVICE_ID: device.id, # type: ignore[union-attr]
|
||||
CONF_UNIQUE_ID: hue_resource.id,
|
||||
CONF_TYPE: hue_resource.relative_rotary.last_event.action.value,
|
||||
CONF_TYPE: hue_resource.relative_rotary.rotary_report.action.value,
|
||||
CONF_SUBTYPE: hue_resource.relative_rotary.last_event.rotation.direction.value,
|
||||
CONF_DURATION: hue_resource.relative_rotary.last_event.rotation.duration,
|
||||
CONF_STEPS: hue_resource.relative_rotary.last_event.rotation.steps,
|
||||
|
@ -48,7 +48,7 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aiolifx", "aiolifx_effects", "bitstring"],
|
||||
"requirements": [
|
||||
"aiolifx==1.0.5",
|
||||
"aiolifx==1.0.6",
|
||||
"aiolifx-effects==0.3.2",
|
||||
"aiolifx-themes==0.4.15"
|
||||
]
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import mimetypes
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from mastodon import Mastodon
|
||||
from mastodon.Mastodon import MastodonAPIError, MastodonUnauthorizedError
|
||||
@ -71,11 +71,15 @@ class MastodonNotificationService(BaseNotificationService):
|
||||
|
||||
def send_message(self, message: str = "", **kwargs: Any) -> None:
|
||||
"""Toot a message, with media perhaps."""
|
||||
|
||||
target = None
|
||||
if (target_list := kwargs.get(ATTR_TARGET)) is not None:
|
||||
target = cast(list[str], target_list)[0]
|
||||
|
||||
data = kwargs.get(ATTR_DATA)
|
||||
|
||||
media = None
|
||||
mediadata = None
|
||||
target = None
|
||||
sensitive = False
|
||||
content_warning = None
|
||||
|
||||
@ -87,7 +91,6 @@ class MastodonNotificationService(BaseNotificationService):
|
||||
return
|
||||
mediadata = self._upload_media(media)
|
||||
|
||||
target = data.get(ATTR_TARGET)
|
||||
sensitive = data.get(ATTR_MEDIA_WARNING)
|
||||
content_warning = data.get(ATTR_CONTENT_WARNING)
|
||||
|
||||
|
@ -168,10 +168,10 @@ class MatterLock(MatterEntity, LockEntity):
|
||||
|
||||
LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)
|
||||
|
||||
if lock_state is clusters.DoorLock.Enums.DlLockState.kUnlatched:
|
||||
if lock_state == clusters.DoorLock.Enums.DlLockState.kUnlatched:
|
||||
self._attr_is_locked = False
|
||||
self._attr_is_open = True
|
||||
if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
|
||||
elif lock_state == clusters.DoorLock.Enums.DlLockState.kLocked:
|
||||
self._attr_is_locked = True
|
||||
self._attr_is_open = False
|
||||
elif lock_state in (
|
||||
|
@ -251,7 +251,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
mqtt_data.client.async_restore_tracked_subscriptions(
|
||||
mqtt_data.subscriptions_to_restore
|
||||
)
|
||||
mqtt_data.subscriptions_to_restore = []
|
||||
mqtt_data.subscriptions_to_restore = set()
|
||||
mqtt_data.reload_dispatchers.append(
|
||||
entry.add_update_listener(_async_config_entry_updated)
|
||||
)
|
||||
|
@ -428,12 +428,12 @@ class MQTT:
|
||||
await self.async_init_client()
|
||||
|
||||
@property
|
||||
def subscriptions(self) -> list[Subscription]:
|
||||
def subscriptions(self) -> set[Subscription]:
|
||||
"""Return the tracked subscriptions."""
|
||||
return [
|
||||
return {
|
||||
*chain.from_iterable(self._simple_subscriptions.values()),
|
||||
*self._wildcard_subscriptions,
|
||||
]
|
||||
}
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""Clean up listeners."""
|
||||
@ -736,7 +736,7 @@ class MQTT:
|
||||
|
||||
@callback
|
||||
def async_restore_tracked_subscriptions(
|
||||
self, subscriptions: list[Subscription]
|
||||
self, subscriptions: set[Subscription]
|
||||
) -> None:
|
||||
"""Restore tracked subscriptions after reload."""
|
||||
for subscription in subscriptions:
|
||||
|
@ -423,7 +423,7 @@ class MqttData:
|
||||
reload_handlers: dict[str, CALLBACK_TYPE] = field(default_factory=dict)
|
||||
reload_schema: dict[str, VolSchemaType] = field(default_factory=dict)
|
||||
state_write_requests: EntityTopicState = field(default_factory=EntityTopicState)
|
||||
subscriptions_to_restore: list[Subscription] = field(default_factory=list)
|
||||
subscriptions_to_restore: set[Subscription] = field(default_factory=set)
|
||||
tags: dict[str, dict[str, MQTTTagScanner]] = field(default_factory=dict)
|
||||
|
||||
|
||||
|
@ -18,5 +18,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/reolink",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["reolink_aio"],
|
||||
"requirements": ["reolink-aio==0.9.4"]
|
||||
"requirements": ["reolink-aio==0.9.5"]
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ async def _generate_trackables(
|
||||
trackable = await trackable.details()
|
||||
|
||||
# Check that the pet has tracker linked.
|
||||
if not trackable["device_id"]:
|
||||
if not trackable.get("device_id"):
|
||||
return None
|
||||
|
||||
if "details" not in trackable:
|
||||
|
@ -7,5 +7,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["aiotractive"],
|
||||
"requirements": ["aiotractive==0.5.6"]
|
||||
"requirements": ["aiotractive==0.6.0"]
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="air_temp",
|
||||
translation_key="air_temperature",
|
||||
value_fn=lambda data: data.air_temp or 0,
|
||||
value_fn=lambda data: data.air_temp,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@ -69,7 +69,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="road_temp",
|
||||
translation_key="road_temperature",
|
||||
value_fn=lambda data: data.road_temp or 0,
|
||||
value_fn=lambda data: data.road_temp,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@ -91,7 +91,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
),
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="wind_speed",
|
||||
value_fn=lambda data: data.windforce or 0,
|
||||
value_fn=lambda data: data.windforce,
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@ -99,7 +99,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="wind_speed_max",
|
||||
translation_key="wind_speed_max",
|
||||
value_fn=lambda data: data.windforcemax or 0,
|
||||
value_fn=lambda data: data.windforcemax,
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
entity_registry_enabled_default=False,
|
||||
@ -107,7 +107,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
),
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="humidity",
|
||||
value_fn=lambda data: data.humidity or 0,
|
||||
value_fn=lambda data: data.humidity,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
@ -115,7 +115,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
),
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="precipitation_amount",
|
||||
value_fn=lambda data: data.precipitation_amount or 0,
|
||||
value_fn=lambda data: data.precipitation_amount,
|
||||
native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@ -130,7 +130,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = (
|
||||
TrafikverketSensorEntityDescription(
|
||||
key="dew_point",
|
||||
translation_key="dew_point",
|
||||
value_fn=lambda data: data.dew_point or 0,
|
||||
value_fn=lambda data: data.dew_point,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
|
@ -950,6 +950,8 @@ class ViCareSensor(ViCareEntity, SensorEntity):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(device_config, api, description.key)
|
||||
self.entity_description = description
|
||||
# run update to have device_class set depending on unit_of_measurement
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
@ -579,6 +579,15 @@ DISCOVERY_SCHEMAS = [
|
||||
),
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
# ZVIDAR Z-CM-V01 (SmartWings/Deyi WM25L/V Z-Wave Motor for Roller Shade)
|
||||
ZWaveDiscoverySchema(
|
||||
platform=Platform.COVER,
|
||||
hint="shade",
|
||||
manufacturer_id={0x045A},
|
||||
product_id={0x0507},
|
||||
product_type={0x0904},
|
||||
primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
||||
),
|
||||
# Vision Security ZL7432 In Wall Dual Relay Switch
|
||||
ZWaveDiscoverySchema(
|
||||
platform=Platform.SWITCH,
|
||||
|
@ -24,7 +24,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2024
|
||||
MINOR_VERSION: Final = 7
|
||||
PATCH_VERSION: Final = "3"
|
||||
PATCH_VERSION: Final = "4"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||
|
@ -615,6 +615,9 @@ class ScriptTool(Tool):
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
self.name = split_entity_id(script_entity_id)[1]
|
||||
if self.name[0].isdigit():
|
||||
self.name = "_" + self.name
|
||||
self._entity_id = script_entity_id
|
||||
self.parameters = vol.Schema({})
|
||||
entity_entry = entity_registry.async_get(script_entity_id)
|
||||
if entity_entry and entity_entry.unique_id:
|
||||
@ -715,7 +718,7 @@ class ScriptTool(Tool):
|
||||
SCRIPT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: SCRIPT_DOMAIN + "." + self.name,
|
||||
ATTR_ENTITY_ID: self._entity_id,
|
||||
ATTR_VARIABLES: tool_input.tool_args,
|
||||
},
|
||||
context=llm_context.context,
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2024.7.3"
|
||||
version = "2024.7.4"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
@ -264,7 +264,7 @@ aioharmony==0.2.10
|
||||
aiohomekit==3.1.5
|
||||
|
||||
# homeassistant.components.hue
|
||||
aiohue==4.7.1
|
||||
aiohue==4.7.2
|
||||
|
||||
# homeassistant.components.imap
|
||||
aioimaplib==1.1.0
|
||||
@ -282,7 +282,7 @@ aiolifx-effects==0.3.2
|
||||
aiolifx-themes==0.4.15
|
||||
|
||||
# homeassistant.components.lifx
|
||||
aiolifx==1.0.5
|
||||
aiolifx==1.0.6
|
||||
|
||||
# homeassistant.components.livisi
|
||||
aiolivisi==0.0.19
|
||||
@ -386,7 +386,7 @@ aiosyncthing==0.5.1
|
||||
aiotankerkoenig==0.4.1
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==0.5.6
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==79
|
||||
@ -709,7 +709,7 @@ debugpy==1.8.1
|
||||
# decora==0.6
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==8.1.1
|
||||
deebot-client==8.2.0
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@ -2460,7 +2460,7 @@ renault-api==0.2.4
|
||||
renson-endura-delta==1.7.1
|
||||
|
||||
# homeassistant.components.reolink
|
||||
reolink-aio==0.9.4
|
||||
reolink-aio==0.9.5
|
||||
|
||||
# homeassistant.components.idteck_prox
|
||||
rfk101py==0.0.1
|
||||
|
@ -240,7 +240,7 @@ aioharmony==0.2.10
|
||||
aiohomekit==3.1.5
|
||||
|
||||
# homeassistant.components.hue
|
||||
aiohue==4.7.1
|
||||
aiohue==4.7.2
|
||||
|
||||
# homeassistant.components.imap
|
||||
aioimaplib==1.1.0
|
||||
@ -255,7 +255,7 @@ aiolifx-effects==0.3.2
|
||||
aiolifx-themes==0.4.15
|
||||
|
||||
# homeassistant.components.lifx
|
||||
aiolifx==1.0.5
|
||||
aiolifx==1.0.6
|
||||
|
||||
# homeassistant.components.livisi
|
||||
aiolivisi==0.0.19
|
||||
@ -359,7 +359,7 @@ aiosyncthing==0.5.1
|
||||
aiotankerkoenig==0.4.1
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==0.5.6
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==79
|
||||
@ -590,7 +590,7 @@ dbus-fast==2.22.1
|
||||
debugpy==1.8.1
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==8.1.1
|
||||
deebot-client==8.2.0
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@ -1924,7 +1924,7 @@ renault-api==0.2.4
|
||||
renson-endura-delta==1.7.1
|
||||
|
||||
# homeassistant.components.reolink
|
||||
reolink-aio==0.9.4
|
||||
reolink-aio==0.9.5
|
||||
|
||||
# homeassistant.components.rflink
|
||||
rflink==0.0.66
|
||||
|
@ -442,6 +442,24 @@
|
||||
description: "Test function"
|
||||
parameters {
|
||||
type_: OBJECT
|
||||
properties {
|
||||
key: "param3"
|
||||
value {
|
||||
type_: OBJECT
|
||||
properties {
|
||||
key: "json"
|
||||
value {
|
||||
type_: STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
properties {
|
||||
key: "param2"
|
||||
value {
|
||||
type_: NUMBER
|
||||
}
|
||||
}
|
||||
properties {
|
||||
key: "param1"
|
||||
value {
|
||||
@ -449,7 +467,6 @@
|
||||
description: "Test parameters"
|
||||
items {
|
||||
type_: STRING
|
||||
format_: "lower"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,9 @@ async def test_function_call(
|
||||
{
|
||||
vol.Optional("param1", description="Test parameters"): [
|
||||
vol.All(str, vol.Lower)
|
||||
]
|
||||
],
|
||||
vol.Optional("param2"): vol.Any(float, int),
|
||||
vol.Optional("param3"): dict,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1487,6 +1487,10 @@
|
||||
"on": {
|
||||
"on": true
|
||||
},
|
||||
"owner": {
|
||||
"rid": "7cee478d-6455-483a-9e32-9f9fdcbcc4f6",
|
||||
"rtype": "zone"
|
||||
},
|
||||
"type": "grouped_light"
|
||||
},
|
||||
{
|
||||
@ -1498,6 +1502,10 @@
|
||||
"on": {
|
||||
"on": true
|
||||
},
|
||||
"owner": {
|
||||
"rid": "7cee478d-6455-483a-9e32-9f9fdcbcc4f6",
|
||||
"rtype": "zone"
|
||||
},
|
||||
"type": "grouped_light"
|
||||
},
|
||||
{
|
||||
@ -1509,6 +1517,10 @@
|
||||
"on": {
|
||||
"on": false
|
||||
},
|
||||
"owner": {
|
||||
"rid": "7cee478d-6455-483a-9e32-9f9fdcbcc4f6",
|
||||
"rtype": "zone"
|
||||
},
|
||||
"type": "grouped_light"
|
||||
},
|
||||
{
|
||||
|
@ -28,7 +28,12 @@ async def test_hue_event(
|
||||
|
||||
# Emit button update event
|
||||
btn_event = {
|
||||
"button": {"last_event": "initial_press"},
|
||||
"button": {
|
||||
"button_report": {
|
||||
"event": "initial_press",
|
||||
"updated": "2021-10-01T12:00:00Z",
|
||||
}
|
||||
},
|
||||
"id": "c658d3d8-a013-4b81-8ac6-78b248537e70",
|
||||
"metadata": {"control_id": 1},
|
||||
"type": "button",
|
||||
@ -41,7 +46,7 @@ async def test_hue_event(
|
||||
assert len(events) == 1
|
||||
assert events[0].data["id"] == "wall_switch_with_2_controls_button"
|
||||
assert events[0].data["unique_id"] == btn_event["id"]
|
||||
assert events[0].data["type"] == btn_event["button"]["last_event"]
|
||||
assert events[0].data["type"] == btn_event["button"]["button_report"]["event"]
|
||||
assert events[0].data["subtype"] == btn_event["metadata"]["control_id"]
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@ import pytest
|
||||
|
||||
from homeassistant.components.lock import (
|
||||
STATE_LOCKED,
|
||||
STATE_OPEN,
|
||||
STATE_UNLOCKED,
|
||||
LockEntityFeature,
|
||||
)
|
||||
@ -82,12 +83,12 @@ async def test_lock(
|
||||
assert state
|
||||
assert state.state == STATE_UNLOCKED
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, 0)
|
||||
set_node_attribute(door_lock, 1, 257, 0, 1)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNLOCKED
|
||||
assert state.state == STATE_LOCKED
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, None)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
@ -213,9 +214,16 @@ async def test_lock_with_unbolt(
|
||||
assert state
|
||||
assert state.state == STATE_OPENING
|
||||
|
||||
set_node_attribute(door_lock_with_unbolt, 1, 257, 3, 0)
|
||||
set_node_attribute(door_lock_with_unbolt, 1, 257, 0, 0)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_LOCKED
|
||||
assert state.state == STATE_UNLOCKED
|
||||
|
||||
set_node_attribute(door_lock_with_unbolt, 1, 257, 0, 3)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_OPEN
|
@ -472,6 +472,12 @@ def iblinds_v3_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/cover_iblinds_v3_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="zvidar_state", scope="package")
|
||||
def zvidar_state_fixture():
|
||||
"""Load the ZVIDAR node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/cover_zvidar_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="qubino_shutter_state", scope="package")
|
||||
def qubino_shutter_state_fixture():
|
||||
"""Load the Qubino Shutter node state fixture data."""
|
||||
@ -1081,6 +1087,14 @@ def iblinds_v3_cover_fixture(client, iblinds_v3_state):
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="zvidar")
|
||||
def zvidar_cover_fixture(client, zvidar_state):
|
||||
"""Mock a ZVIDAR window cover node."""
|
||||
node = Node(client, copy.deepcopy(zvidar_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="qubino_shutter")
|
||||
def qubino_shutter_cover_fixture(client, qubino_shutter_state):
|
||||
"""Mock a Qubino flush shutter node."""
|
||||
|
1120
tests/components/zwave_js/fixtures/cover_zvidar_state.json
Normal file
1120
tests/components/zwave_js/fixtures/cover_zvidar_state.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,18 @@ async def test_iblinds_v2(hass: HomeAssistant, client, iblinds_v2, integration)
|
||||
assert state
|
||||
|
||||
|
||||
async def test_zvidar_state(hass: HomeAssistant, client, zvidar, integration) -> None:
|
||||
"""Test that an ZVIDAR Z-CM-V01 multilevel switch value is discovered as a cover."""
|
||||
node = zvidar
|
||||
assert node.device_class.specific.label == "Unused"
|
||||
|
||||
state = hass.states.get("light.window_blind_controller")
|
||||
assert not state
|
||||
|
||||
state = hass.states.get("cover.window_blind_controller")
|
||||
assert state
|
||||
|
||||
|
||||
async def test_ge_12730(hass: HomeAssistant, client, ge_12730, integration) -> None:
|
||||
"""Test GE 12730 Fan Controller v2.0 multilevel switch is discovered as a fan."""
|
||||
node = ge_12730
|
||||
|
@ -780,6 +780,46 @@ async def test_script_tool(
|
||||
}
|
||||
|
||||
|
||||
async def test_script_tool_name(hass: HomeAssistant) -> None:
|
||||
"""Test that script tool name is not started with a digit."""
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
context = Context()
|
||||
llm_context = llm.LLMContext(
|
||||
platform="test_platform",
|
||||
context=context,
|
||||
user_prompt="test_text",
|
||||
language="*",
|
||||
assistant="conversation",
|
||||
device_id=None,
|
||||
)
|
||||
|
||||
# Create a script with a unique ID
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"script",
|
||||
{
|
||||
"script": {
|
||||
"123456": {
|
||||
"description": "This is a test script",
|
||||
"sequence": [],
|
||||
"fields": {
|
||||
"beer": {"description": "Number of beers", "required": True},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
async_expose_entity(hass, "conversation", "script.123456", True)
|
||||
|
||||
api = await llm.async_get_api(hass, "assist", llm_context)
|
||||
|
||||
tools = [tool for tool in api.tools if isinstance(tool, llm.ScriptTool)]
|
||||
assert len(tools) == 1
|
||||
|
||||
tool = tools[0]
|
||||
assert tool.name == "_123456"
|
||||
|
||||
|
||||
async def test_selector_serializer(
|
||||
hass: HomeAssistant, llm_context: llm.LLMContext
|
||||
) -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user