diff --git a/homeassistant/components/cync/manifest.json b/homeassistant/components/cync/manifest.json index d02b6ed1d9b1bd..b61a3165a1db2a 100644 --- a/homeassistant/components/cync/manifest.json +++ b/homeassistant/components/cync/manifest.json @@ -7,5 +7,5 @@ "integration_type": "hub", "iot_class": "cloud_push", "quality_scale": "bronze", - "requirements": ["pycync==0.4.0"] + "requirements": ["pycync==0.4.1"] } diff --git a/homeassistant/components/devolo_home_control/binary_sensor.py b/homeassistant/components/devolo_home_control/binary_sensor.py index 7a88b12c48a9fc..ef80005a9048ea 100644 --- a/homeassistant/components/devolo_home_control/binary_sensor.py +++ b/homeassistant/components/devolo_home_control/binary_sensor.py @@ -126,7 +126,7 @@ def __init__( self._attr_translation_key = "button" self._attr_translation_placeholders = {"key": str(key)} - def _sync(self, message: tuple) -> None: + def sync_callback(self, message: tuple) -> None: """Update the binary sensor state.""" if ( message[0] == self._remote_control_property.element_uid diff --git a/homeassistant/components/devolo_home_control/entity.py b/homeassistant/components/devolo_home_control/entity.py index dade8d6a2f97ab..ab9f29873cde65 100644 --- a/homeassistant/components/devolo_home_control/entity.py +++ b/homeassistant/components/devolo_home_control/entity.py @@ -48,7 +48,6 @@ def __init__( ) self.subscriber: Subscriber | None = None - self.sync_callback = self._sync self._value: float @@ -69,7 +68,7 @@ async def async_will_remove_from_hass(self) -> None: self._device_instance.uid, self.subscriber ) - def _sync(self, message: tuple) -> None: + def sync_callback(self, message: tuple) -> None: """Update the state.""" if message[0] == self._attr_unique_id: self._value = message[1] diff --git a/homeassistant/components/devolo_home_control/sensor.py b/homeassistant/components/devolo_home_control/sensor.py index 22581267eea3ea..e601728d851956 100644 --- a/homeassistant/components/devolo_home_control/sensor.py +++ b/homeassistant/components/devolo_home_control/sensor.py @@ -185,7 +185,7 @@ def unique_id(self) -> str: """ return f"{self._attr_unique_id}_{self._sensor_type}" - def _sync(self, message: tuple) -> None: + def sync_callback(self, message: tuple) -> None: """Update the consumption sensor state.""" if message[0] == self._attr_unique_id: self._value = getattr( diff --git a/homeassistant/components/devolo_home_control/subscriber.py b/homeassistant/components/devolo_home_control/subscriber.py index 99c21b3fd36e8f..5493bdea16591a 100644 --- a/homeassistant/components/devolo_home_control/subscriber.py +++ b/homeassistant/components/devolo_home_control/subscriber.py @@ -13,8 +13,3 @@ def __init__(self, name: str, callback: Callable) -> None: """Initiate the subscriber.""" self.name = name self.callback = callback - - def update(self, message: str) -> None: - """Trigger hass to update the device.""" - _LOGGER.debug('%s got message "%s"', self.name, message) - self.callback(message) diff --git a/homeassistant/components/devolo_home_control/switch.py b/homeassistant/components/devolo_home_control/switch.py index 378e23a5f5fc43..62f9326bb8980e 100644 --- a/homeassistant/components/devolo_home_control/switch.py +++ b/homeassistant/components/devolo_home_control/switch.py @@ -64,7 +64,7 @@ def turn_off(self, **kwargs: Any) -> None: """Switch off the device.""" self._binary_switch_property.set(state=False) - def _sync(self, message: tuple) -> None: + def sync_callback(self, message: tuple) -> None: """Update the binary switch state and consumption.""" if message[0].startswith("devolo.BinarySwitch"): self._attr_is_on = self._device_instance.binary_switch_property[ diff --git a/homeassistant/components/google_generative_ai_conversation/entity.py b/homeassistant/components/google_generative_ai_conversation/entity.py index 45ef4aad2d44d1..74b76d9bb83464 100644 --- a/homeassistant/components/google_generative_ai_conversation/entity.py +++ b/homeassistant/components/google_generative_ai_conversation/entity.py @@ -620,6 +620,13 @@ async def _async_handle_chat_log( def create_generate_content_config(self) -> GenerateContentConfig: """Create the GenerateContentConfig for the LLM.""" options = self.subentry.data + model = options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL) + thinking_config: ThinkingConfig | None = None + if model.startswith("models/gemini-2.5") and not model.endswith( + ("tts", "image", "image-preview") + ): + thinking_config = ThinkingConfig(include_thoughts=True) + return GenerateContentConfig( temperature=options.get(CONF_TEMPERATURE, RECOMMENDED_TEMPERATURE), top_k=options.get(CONF_TOP_K, RECOMMENDED_TOP_K), @@ -652,7 +659,7 @@ def create_generate_content_config(self) -> GenerateContentConfig: ), ), ], - thinking_config=ThinkingConfig(include_thoughts=True), + thinking_config=thinking_config, ) diff --git a/homeassistant/components/habitica/icons.json b/homeassistant/components/habitica/icons.json index 9b77606f557921..ee02429d3718e4 100644 --- a/homeassistant/components/habitica/icons.json +++ b/homeassistant/components/habitica/icons.json @@ -174,6 +174,9 @@ }, "collected_items": { "default": "mdi:sack" + }, + "last_checkin": { + "default": "mdi:login-variant" } }, "switch": { diff --git a/homeassistant/components/habitica/sensor.py b/homeassistant/components/habitica/sensor.py index a13594e6f4bcf4..d13b5562cd6c58 100644 --- a/homeassistant/components/habitica/sensor.py +++ b/homeassistant/components/habitica/sensor.py @@ -4,6 +4,7 @@ from collections.abc import Callable from dataclasses import dataclass +from datetime import datetime from enum import StrEnum import logging from typing import Any @@ -53,7 +54,7 @@ class HabiticaSensorEntityDescription(SensorEntityDescription): """Habitica Sensor Description.""" - value_fn: Callable[[UserData, ContentData], StateType] + value_fn: Callable[[UserData, ContentData], StateType | datetime] attributes_fn: Callable[[UserData, ContentData], dict[str, Any] | None] | None = ( None ) @@ -114,6 +115,7 @@ class HabiticaSensorEntity(StrEnum): COLLECTED_ITEMS = "collected_items" BOSS_RAGE = "boss_rage" BOSS_RAGE_LIMIT = "boss_rage_limit" + LAST_CHECKIN = "last_checkin" SENSOR_DESCRIPTIONS: tuple[HabiticaSensorEntityDescription, ...] = ( @@ -284,6 +286,16 @@ class HabiticaSensorEntity(StrEnum): translation_key=HabiticaSensorEntity.PENDING_QUEST_ITEMS, value_fn=pending_quest_items, ), + HabiticaSensorEntityDescription( + key=HabiticaSensorEntity.LAST_CHECKIN, + translation_key=HabiticaSensorEntity.LAST_CHECKIN, + value_fn=( + lambda user, _: dt_util.as_local(last) + if (last := user.auth.timestamps.loggedin) + else None + ), + device_class=SensorDeviceClass.TIMESTAMP, + ), ) @@ -399,7 +411,7 @@ class HabiticaSensor(HabiticaBase, SensorEntity): entity_description: HabiticaSensorEntityDescription @property - def native_value(self) -> StateType: + def native_value(self) -> StateType | datetime: """Return the state of the device.""" return self.entity_description.value_fn( @@ -442,7 +454,7 @@ class HabiticaPartySensor(HabiticaPartyBase, SensorEntity): entity_description: HabiticaPartySensorEntityDescription @property - def native_value(self) -> StateType: + def native_value(self) -> StateType | datetime: """Return the state of the device.""" return self.entity_description.value_fn( diff --git a/homeassistant/components/habitica/strings.json b/homeassistant/components/habitica/strings.json index 57c5fee55b650a..53e570bd978a9c 100644 --- a/homeassistant/components/habitica/strings.json +++ b/homeassistant/components/habitica/strings.json @@ -290,6 +290,9 @@ } } }, + "last_checkin": { + "name": "Last check-in" + }, "health": { "name": "Health", "unit_of_measurement": "[%key:component::habitica::common::unit_health_points%]" diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 108303d9d3de6b..1acaae2b583426 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -14,6 +14,6 @@ "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "iot_class": "local_push", "loggers": ["aiohomekit", "commentjson"], - "requirements": ["aiohomekit==3.2.18"], + "requirements": ["aiohomekit==3.2.19"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] } diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index 6bdcd1e0f9d73f..86d59e0dc6c0bc 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -307,7 +307,7 @@ }, "markdown": { "name": "Format as Markdown", - "description": "Enable Markdown formatting for the message body (Web app only). See the Markdown guide for syntax details: https://www.markdownguide.org/basic-syntax/." + "description": "Enable Markdown formatting for the message body. See the Markdown guide for syntax details: https://www.markdownguide.org/basic-syntax/." }, "tags": { "name": "Tags/Emojis", diff --git a/homeassistant/components/onedrive/backup.py b/homeassistant/components/onedrive/backup.py index dfb592c8d458c0..bea1edce692e21 100644 --- a/homeassistant/components/onedrive/backup.py +++ b/homeassistant/components/onedrive/backup.py @@ -35,7 +35,7 @@ from .coordinator import OneDriveConfigEntry _LOGGER = logging.getLogger(__name__) -UPLOAD_CHUNK_SIZE = 16 * 320 * 1024 # 5.2MB +UPLOAD_CHUNK_SIZE = 32 * 320 * 1024 # 10.4MB TIMEOUT = ClientTimeout(connect=10, total=43200) # 12 hours METADATA_VERSION = 2 CACHE_TTL = 300 diff --git a/homeassistant/components/portainer/manifest.json b/homeassistant/components/portainer/manifest.json index 22aea63c129e4c..e907a5fd6dbd4a 100644 --- a/homeassistant/components/portainer/manifest.json +++ b/homeassistant/components/portainer/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/portainer", "iot_class": "local_polling", "quality_scale": "bronze", - "requirements": ["pyportainer==1.0.2"] + "requirements": ["pyportainer==1.0.3"] } diff --git a/homeassistant/components/roborock/manifest.json b/homeassistant/components/roborock/manifest.json index ef129ab5df5b2c..e6bf46e2202af8 100644 --- a/homeassistant/components/roborock/manifest.json +++ b/homeassistant/components/roborock/manifest.json @@ -19,7 +19,7 @@ "loggers": ["roborock"], "quality_scale": "silver", "requirements": [ - "python-roborock==2.47.1", + "python-roborock==2.49.1", "vacuum-map-parser-roborock==0.1.4" ] } diff --git a/homeassistant/components/satel_integra/binary_sensor.py b/homeassistant/components/satel_integra/binary_sensor.py index 7cea005cd5ecdc..fdeef7cffc4c07 100644 --- a/homeassistant/components/satel_integra/binary_sensor.py +++ b/homeassistant/components/satel_integra/binary_sensor.py @@ -13,8 +13,10 @@ from .const import ( CONF_OUTPUT_NUMBER, + CONF_OUTPUTS, CONF_ZONE_NUMBER, CONF_ZONE_TYPE, + CONF_ZONES, SIGNAL_OUTPUTS_UPDATED, SIGNAL_ZONES_UPDATED, SUBENTRY_TYPE_OUTPUT, @@ -49,7 +51,7 @@ async def async_setup_entry( zone_num, zone_name, zone_type, - SUBENTRY_TYPE_ZONE, + CONF_ZONES, SIGNAL_ZONES_UPDATED, ) ], @@ -73,7 +75,7 @@ async def async_setup_entry( output_num, output_name, ouput_type, - SUBENTRY_TYPE_OUTPUT, + CONF_OUTPUTS, SIGNAL_OUTPUTS_UPDATED, ) ], diff --git a/homeassistant/components/togrill/coordinator.py b/homeassistant/components/togrill/coordinator.py index c06888eefb0f18..16b9871dd3e9bf 100644 --- a/homeassistant/components/togrill/coordinator.py +++ b/homeassistant/components/togrill/coordinator.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio from collections.abc import Callable from datetime import timedelta import logging @@ -146,8 +147,9 @@ async def _connect_and_update_registry(self) -> Client: raise DeviceNotFound("Unable to connect to device") from exc try: - packet_a0 = await client.read(PacketA0Notify) - except (BleakError, DecodeError) as exc: + async with asyncio.timeout(10): + packet_a0 = await client.read(PacketA0Notify) + except (BleakError, DecodeError, TimeoutError) as exc: await client.disconnect() raise DeviceFailed(f"Device failed {exc}") from exc @@ -212,9 +214,19 @@ async def _async_update_data(self) -> dict[tuple[int, int | None], Packet]: @callback def _async_request_refresh_soon(self) -> None: - self.config_entry.async_create_task( - self.hass, self.async_request_refresh(), eager_start=False - ) + """Request a refresh in the near future. + + This way have been called during an update and + would be ignored by debounce logic, so we delay + it by a slight amount to hopefully let the current + update finish first. + """ + + async def _delayed_refresh() -> None: + await asyncio.sleep(0.5) + await self.async_request_refresh() + + self.config_entry.async_create_task(self.hass, _delayed_refresh()) @callback def _disconnected_callback(self) -> None: diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index b94530e432b011..1f1f1ac626aea7 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -704,6 +704,7 @@ class DPCode(StrEnum): DEHUMIDITY_SET_ENUM = "dehumidify_set_enum" DEHUMIDITY_SET_VALUE = "dehumidify_set_value" DELAY_SET = "delay_set" + DEW_POINT_TEMP = "dew_point_temp" DISINFECTION = "disinfection" DO_NOT_DISTURB = "do_not_disturb" DOORCONTACT_STATE = "doorcontact_state" # Status of door window sensor @@ -728,6 +729,7 @@ class DPCode(StrEnum): FEED_REPORT = "feed_report" FEED_STATE = "feed_state" FEEDIN_POWER_LIMIT_ENABLE = "feedin_power_limit_enable" + FEELLIKE_TEMP = "feellike_temp" FILTER = "filter" FILTER_DURATION = "filter_life" # Filter duration (hours) FILTER_LIFE = "filter" # Filter life (percentage) @@ -739,6 +741,7 @@ class DPCode(StrEnum): GAS_SENSOR_STATE = "gas_sensor_state" GAS_SENSOR_STATUS = "gas_sensor_status" GAS_SENSOR_VALUE = "gas_sensor_value" + HEAT_INDEX = "heat_index" HUMIDIFIER = "humidifier" # Humidification HUMIDITY = "humidity" # Humidity HUMIDITY_CURRENT = "humidity_current" # Current humidity @@ -968,6 +971,7 @@ class DPCode(StrEnum): WET = "wet" # Humidification WINDOW_CHECK = "window_check" WINDOW_STATE = "window_state" + WINDCHILL_INDEX = "windchill_index" WINDSPEED = "windspeed" WINDSPEED_AVG = "windspeed_avg" WIND_DIRECT = "wind_direct" diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index f00b034c8a2487..0ad28cbc096470 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -960,6 +960,30 @@ class TuyaSensorEntityDescription(SensorEntityDescription): state_class=SensorStateClass.MEASUREMENT, state_conversion=lambda state: _WIND_DIRECTIONS.get(str(state)), ), + TuyaSensorEntityDescription( + key=DPCode.DEW_POINT_TEMP, + translation_key="dew_point_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.FEELLIKE_TEMP, + translation_key="feels_like_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.HEAT_INDEX, + translation_key="heat_index_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.WINDCHILL_INDEX, + translation_key="wind_chill_index_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), *BATTERY_SENSORS, ), DeviceCategory.RQBJ: ( diff --git a/homeassistant/components/tuya/strings.json b/homeassistant/components/tuya/strings.json index b5b543f4fa3d79..f7eb9f43be4bc5 100644 --- a/homeassistant/components/tuya/strings.json +++ b/homeassistant/components/tuya/strings.json @@ -853,6 +853,18 @@ }, "total_dissolved_solids": { "name": "Total dissolved solids" + }, + "dew_point_temperature": { + "name": "Dew point" + }, + "feels_like_temperature": { + "name": "Feels like" + }, + "heat_index_temperature": { + "name": "Heat index" + }, + "wind_chill_index_temperature": { + "name": "Wind chill index" } }, "switch": { diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 081b7a15995f69..13389c6e797194 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -104,41 +104,6 @@ class VacuumEntityFeature(IntFlag): START = 8192 -# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5. -# Please use the VacuumEntityFeature enum instead. -_DEPRECATED_SUPPORT_TURN_ON = DeprecatedConstantEnum( - VacuumEntityFeature.TURN_ON, "2025.10" -) -_DEPRECATED_SUPPORT_TURN_OFF = DeprecatedConstantEnum( - VacuumEntityFeature.TURN_OFF, "2025.10" -) -_DEPRECATED_SUPPORT_PAUSE = DeprecatedConstantEnum(VacuumEntityFeature.PAUSE, "2025.10") -_DEPRECATED_SUPPORT_STOP = DeprecatedConstantEnum(VacuumEntityFeature.STOP, "2025.10") -_DEPRECATED_SUPPORT_RETURN_HOME = DeprecatedConstantEnum( - VacuumEntityFeature.RETURN_HOME, "2025.10" -) -_DEPRECATED_SUPPORT_FAN_SPEED = DeprecatedConstantEnum( - VacuumEntityFeature.FAN_SPEED, "2025.10" -) -_DEPRECATED_SUPPORT_BATTERY = DeprecatedConstantEnum( - VacuumEntityFeature.BATTERY, "2025.10" -) -_DEPRECATED_SUPPORT_STATUS = DeprecatedConstantEnum( - VacuumEntityFeature.STATUS, "2025.10" -) -_DEPRECATED_SUPPORT_SEND_COMMAND = DeprecatedConstantEnum( - VacuumEntityFeature.SEND_COMMAND, "2025.10" -) -_DEPRECATED_SUPPORT_LOCATE = DeprecatedConstantEnum( - VacuumEntityFeature.LOCATE, "2025.10" -) -_DEPRECATED_SUPPORT_CLEAN_SPOT = DeprecatedConstantEnum( - VacuumEntityFeature.CLEAN_SPOT, "2025.10" -) -_DEPRECATED_SUPPORT_MAP = DeprecatedConstantEnum(VacuumEntityFeature.MAP, "2025.10") -_DEPRECATED_SUPPORT_STATE = DeprecatedConstantEnum(VacuumEntityFeature.STATE, "2025.10") -_DEPRECATED_SUPPORT_START = DeprecatedConstantEnum(VacuumEntityFeature.START, "2025.10") - # mypy: disallow-any-generics diff --git a/requirements_all.txt b/requirements_all.txt index 593be74d597ac3..137a7e60198c2b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -271,7 +271,7 @@ aiohasupervisor==0.3.3 aiohomeconnect==0.20.0 # homeassistant.components.homekit_controller -aiohomekit==3.2.18 +aiohomekit==3.2.19 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -1936,7 +1936,7 @@ pycsspeechtts==1.0.8 # pycups==2.0.4 # homeassistant.components.cync -pycync==0.4.0 +pycync==0.4.1 # homeassistant.components.daikin pydaikin==2.16.0 @@ -2293,7 +2293,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.2 +pyportainer==1.0.3 # homeassistant.components.probe_plus pyprobeplus==1.0.1 @@ -2547,7 +2547,7 @@ python-rabbitair==0.0.8 python-ripple-api==0.0.3 # homeassistant.components.roborock -python-roborock==2.47.1 +python-roborock==2.49.1 # homeassistant.components.smarttub python-smarttub==0.0.44 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b77ebe6937d7b8..c845c3a3a8d745 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -256,7 +256,7 @@ aiohasupervisor==0.3.3 aiohomeconnect==0.20.0 # homeassistant.components.homekit_controller -aiohomekit==3.2.18 +aiohomekit==3.2.19 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -1626,7 +1626,7 @@ pycsspeechtts==1.0.8 # pycups==2.0.4 # homeassistant.components.cync -pycync==0.4.0 +pycync==0.4.1 # homeassistant.components.daikin pydaikin==2.16.0 @@ -1917,7 +1917,7 @@ pyplaato==0.0.19 pypoint==3.0.0 # homeassistant.components.portainer -pyportainer==1.0.2 +pyportainer==1.0.3 # homeassistant.components.probe_plus pyprobeplus==1.0.1 @@ -2117,7 +2117,7 @@ python-pooldose==0.5.0 python-rabbitair==0.0.8 # homeassistant.components.roborock -python-roborock==2.47.1 +python-roborock==2.49.1 # homeassistant.components.smarttub python-smarttub==0.0.44 diff --git a/tests/components/google_generative_ai_conversation/test_tts.py b/tests/components/google_generative_ai_conversation/test_tts.py index 271a209f79f33f..c55a2a2795d369 100644 --- a/tests/components/google_generative_ai_conversation/test_tts.py +++ b/tests/components/google_generative_ai_conversation/test_tts.py @@ -208,7 +208,7 @@ async def test_tts_service_speak( threshold=RECOMMENDED_HARM_BLOCK_THRESHOLD, ), ], - thinking_config=types.ThinkingConfig(include_thoughts=True), + thinking_config=None, ), ) @@ -277,6 +277,6 @@ async def test_tts_service_speak_error( threshold=RECOMMENDED_HARM_BLOCK_THRESHOLD, ), ], - thinking_config=types.ThinkingConfig(include_thoughts=True), + thinking_config=None, ), ) diff --git a/tests/components/habitica/snapshots/test_sensor.ambr b/tests/components/habitica/snapshots/test_sensor.ambr index ae6256b41d634c..07ce6488914513 100644 --- a/tests/components/habitica/snapshots/test_sensor.ambr +++ b/tests/components/habitica/snapshots/test_sensor.ambr @@ -544,6 +544,55 @@ 'state': '72', }) # --- +# name: test_sensors[sensor.test_user_last_check_in-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_user_last_check_in', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Last check-in', + 'platform': 'habitica', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'a380546a-94be-4b8e-8a0b-23e0d5c03303_last_checkin', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[sensor.test_user_last_check_in-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'test-user Last check-in', + }), + 'context': , + 'entity_id': 'sensor.test_user_last_check_in', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2025-02-02T03:14:33+00:00', + }) +# --- # name: test_sensors[sensor.test_user_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/recorder/auto_repairs/statistics/test_duplicates.py b/tests/components/recorder/auto_repairs/statistics/test_duplicates.py index 2466a761364786..91f51b4e0c90e0 100644 --- a/tests/components/recorder/auto_repairs/statistics/test_duplicates.py +++ b/tests/components/recorder/auto_repairs/statistics/test_duplicates.py @@ -19,7 +19,7 @@ from homeassistant.core import HomeAssistant from homeassistant.util import dt as dt_util -from ...common import async_wait_recording_done +from ...common import async_wait_recording_done, get_patched_live_version from tests.common import async_test_home_assistant from tests.typing import RecorderInstanceContextManager @@ -189,6 +189,11 @@ def get_statistics_meta(hass: HomeAssistant) -> list: patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), @@ -309,6 +314,11 @@ def get_statistics_meta(hass: HomeAssistant) -> list: patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index d1e33d3a62699f..094ab11a112ef9 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -11,6 +11,7 @@ import importlib import sys import time +from types import ModuleType from typing import Any, Literal, cast from unittest.mock import MagicMock, patch, sentinel @@ -459,6 +460,13 @@ def get_schema_module_path(schema_version_postfix: str) -> str: return f"tests.components.recorder.db_schema_{schema_version_postfix}" +def get_patched_live_version(old_db_schema: ModuleType) -> int: + """Return the patched live migration version.""" + return min( + migration.LIVE_MIGRATION_MIN_SCHEMA_VERSION, old_db_schema.SCHEMA_VERSION + ) + + @contextmanager def old_db_schema(hass: HomeAssistant, schema_version_postfix: str) -> Iterator[None]: """Fixture to initialize the db with the old schema.""" @@ -469,6 +477,11 @@ def old_db_schema(hass: HomeAssistant, schema_version_postfix: str) -> Iterator[ with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(core, "StatesMeta", old_db_schema.StatesMeta), patch.object(core, "EventTypes", old_db_schema.EventTypes), diff --git a/tests/components/recorder/test_migration_from_schema_32.py b/tests/components/recorder/test_migration_from_schema_32.py index 7fd73aaf735341..2a24b30b7f5565 100644 --- a/tests/components/recorder/test_migration_from_schema_32.py +++ b/tests/components/recorder/test_migration_from_schema_32.py @@ -48,6 +48,7 @@ async_attach_db_engine, async_recorder_block_till_done, async_wait_recording_done, + get_patched_live_version, ) from .conftest import instrument_migration @@ -107,6 +108,11 @@ def db_schema_32(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(core, "StatesMeta", old_db_schema.StatesMeta), patch.object(core, "EventTypes", old_db_schema.EventTypes), @@ -224,6 +230,11 @@ def _insert_events(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventsContextIDMigration, "migrate_data"), patch.object(migration.EventIDPostMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), @@ -285,11 +296,20 @@ def _fetch_migrated_events(): ) as wrapped_idx_create, patch.object(migration.EventIDPostMigration, "migrate_data"), ): + # Stall migration when the last non-live schema migration is done + instrumented_migration.stall_on_schema_version = ( + migration.LIVE_MIGRATION_MIN_SCHEMA_VERSION + ) async with async_test_recorder( hass, wait_recorder=False, wait_recorder_setup=False ) as instance: # Check the context ID migrator is considered non-live assert recorder.util.async_migration_is_live(hass) is False + # Wait for non-live schema migration to complete + await hass.async_add_executor_job( + instrumented_migration.apply_update_stalled.wait + ) + wrapped_idx_create.reset_mock() instrumented_migration.migration_stall.set() instance.recorder_and_worker_thread_ids.add(threading.get_ident()) @@ -422,6 +442,11 @@ def _insert_migration(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventsContextIDMigration, "migrate_data"), patch.object( migration.EventIDPostMigration, @@ -589,6 +614,11 @@ def _insert_states(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.StatesContextIDMigration, "migrate_data"), patch.object(migration.EventIDPostMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), @@ -645,11 +675,20 @@ def _fetch_migrated_states(): ) as wrapped_idx_create, patch.object(migration.EventIDPostMigration, "migrate_data"), ): + # Stall migration when the last non-live schema migration is done + instrumented_migration.stall_on_schema_version = ( + migration.LIVE_MIGRATION_MIN_SCHEMA_VERSION + ) async with async_test_recorder( hass, wait_recorder=False, wait_recorder_setup=False ) as instance: # Check the context ID migrator is considered non-live assert recorder.util.async_migration_is_live(hass) is False + # Wait for non-live schema migration to complete + await hass.async_add_executor_job( + instrumented_migration.apply_update_stalled.wait + ) + wrapped_idx_create.reset_mock() instrumented_migration.migration_stall.set() instance.recorder_and_worker_thread_ids.add(threading.get_ident()) @@ -786,6 +825,11 @@ def _insert_migration(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.StatesContextIDMigration, "migrate_data"), patch.object( migration.EventIDPostMigration, @@ -902,6 +946,11 @@ def _insert_events(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventTypeIDMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): @@ -1020,6 +1069,11 @@ def _insert_states(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EntityIDMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): @@ -1129,6 +1183,11 @@ def _insert_events(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EntityIDMigration, "migrate_data"), patch.object(migration.EntityIDPostMigration, "migrate_data"), patch.object(migration.EventIDPostMigration, "migrate_data"), @@ -1163,36 +1222,49 @@ def _fetch_migrated_states(): return {state.state: state.entity_id for state in states} # Run again with new schema, let migration run - with ( - patch( - "sqlalchemy.schema.Index.create", autospec=True, wraps=Index.create - ) as wrapped_idx_create, - patch.object(migration.EventIDPostMigration, "migrate_data"), - ): - async with ( - async_test_home_assistant() as hass, - async_test_recorder(hass) as instance, + async with async_test_home_assistant() as hass: + with ( + instrument_migration(hass) as instrumented_migration, + patch( + "sqlalchemy.schema.Index.create", autospec=True, wraps=Index.create + ) as wrapped_idx_create, + patch.object(migration.EventIDPostMigration, "migrate_data"), ): - instance.recorder_and_worker_thread_ids.add(threading.get_ident()) + # Stall migration when the last non-live schema migration is done + instrumented_migration.stall_on_schema_version = ( + migration.LIVE_MIGRATION_MIN_SCHEMA_VERSION + ) + async with async_test_recorder( + hass, wait_recorder=False, wait_recorder_setup=False + ) as instance: + # Wait for non-live schema migration to complete + await hass.async_add_executor_job( + instrumented_migration.apply_update_stalled.wait + ) + wrapped_idx_create.reset_mock() + instrumented_migration.migration_stall.set() - await hass.async_block_till_done() - await async_wait_recording_done(hass) + instance.recorder_and_worker_thread_ids.add(threading.get_ident()) - states_by_state = await instance.async_add_executor_job( - _fetch_migrated_states - ) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + await async_wait_recording_done(hass) - # Check the index which will be removed by the migrator no longer exists - with session_scope(hass=hass) as session: - assert ( - get_index_by_name( - session, "states", "ix_states_entity_id_last_updated_ts" - ) - is None + states_by_state = await instance.async_add_executor_job( + _fetch_migrated_states ) - await hass.async_stop() - await hass.async_block_till_done() + # Check the index which will be removed by the migrator no longer exists + with session_scope(hass=hass) as session: + assert ( + get_index_by_name( + session, "states", "ix_states_entity_id_last_updated_ts" + ) + is None + ) + + await hass.async_stop() + await hass.async_block_till_done() # Check the index we removed was recreated index_names = [call[1][0].name for call in wrapped_idx_create.mock_calls] @@ -1242,6 +1314,11 @@ def _insert_states(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EntityIDMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): @@ -1352,6 +1429,11 @@ def _insert_events(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventTypeIDMigration, "migrate_data"), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): @@ -2048,6 +2130,11 @@ def _get_index_names(table): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): @@ -2243,6 +2330,11 @@ def _insert_states(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch(CREATE_ENGINE_TARGET, new=_create_engine_test), ): async with ( @@ -2252,7 +2344,6 @@ def _insert_states(): await instance.async_add_executor_job(_insert_states) await async_wait_recording_done(hass) - now = dt_util.utcnow() await _async_wait_migration_done(hass) await async_wait_recording_done(hass) @@ -2266,29 +2357,22 @@ def _fetch_migrated_states(): return {state.state_id: _object_as_dict(state) for state in states} # Run again with new schema, let migration run - async with async_test_home_assistant() as hass: - with ( - freeze_time(now), - instrument_migration(hass) as instrumented_migration, - ): - async with async_test_recorder( - hass, wait_recorder=False, wait_recorder_setup=False - ) as instance: - # Check the context ID migrator is considered non-live - assert recorder.util.async_migration_is_live(hass) is False - instrumented_migration.migration_stall.set() - instance.recorder_and_worker_thread_ids.add(threading.get_ident()) + async with ( + async_test_home_assistant() as hass, + async_test_recorder(hass) as instance, + ): + instance.recorder_and_worker_thread_ids.add(threading.get_ident()) - await hass.async_block_till_done() - await async_wait_recording_done(hass) - await async_wait_recording_done(hass) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + await async_wait_recording_done(hass) - states_by_metadata_id = await instance.async_add_executor_job( - _fetch_migrated_states - ) + states_by_metadata_id = await instance.async_add_executor_job( + _fetch_migrated_states + ) - await hass.async_stop() - await hass.async_block_till_done() + await hass.async_stop() + await hass.async_block_till_done() assert len(states_by_metadata_id) == 3 for state in states_by_metadata_id.values(): diff --git a/tests/components/recorder/test_migration_run_time_migrations_remember.py b/tests/components/recorder/test_migration_run_time_migrations_remember.py index 350126b4c72f41..69b6c6ee42b4fb 100644 --- a/tests/components/recorder/test_migration_run_time_migrations_remember.py +++ b/tests/components/recorder/test_migration_run_time_migrations_remember.py @@ -22,7 +22,11 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant -from .common import async_recorder_block_till_done, async_wait_recording_done +from .common import ( + async_recorder_block_till_done, + async_wait_recording_done, + get_patched_live_version, +) from tests.common import async_test_home_assistant from tests.typing import RecorderInstanceContextManager @@ -329,6 +333,11 @@ async def test_migration_changes_prevent_trying_to_migrate_again( with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(core, "StatesMeta", old_db_schema.StatesMeta), patch.object(core, "EventTypes", old_db_schema.EventTypes), diff --git a/tests/components/recorder/test_statistics_v23_migration.py b/tests/components/recorder/test_statistics_v23_migration.py index 49b8836af70b69..aac26c2da66c2e 100644 --- a/tests/components/recorder/test_statistics_v23_migration.py +++ b/tests/components/recorder/test_statistics_v23_migration.py @@ -23,6 +23,7 @@ CREATE_ENGINE_TARGET, async_wait_recording_done, create_engine_test_for_schema_version_postfix, + get_patched_live_version, get_schema_module_path, ) @@ -169,6 +170,11 @@ async def test_delete_duplicates( patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), @@ -357,6 +363,11 @@ async def test_delete_duplicates_many( patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), @@ -523,6 +534,11 @@ async def test_delete_duplicates_non_identical( patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), @@ -649,6 +665,11 @@ async def test_delete_duplicates_short_term( patch.object( recorder.migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION ), + patch.object( + recorder.migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object( recorder.migration, "non_live_data_migration_needed", return_value=False ), diff --git a/tests/components/recorder/test_v32_migration.py b/tests/components/recorder/test_v32_migration.py index c4c1285990d8a3..ca7be224381e04 100644 --- a/tests/components/recorder/test_v32_migration.py +++ b/tests/components/recorder/test_v32_migration.py @@ -19,7 +19,7 @@ from homeassistant.core import Event, EventOrigin, State from homeassistant.util import dt as dt_util -from .common import async_wait_recording_done +from .common import async_wait_recording_done, get_patched_live_version from .conftest import instrument_migration from tests.common import async_test_home_assistant @@ -119,6 +119,11 @@ def _get_states_index_names(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(migration, "post_migrate_entity_ids", return_value=False), patch.object(migration.EventsContextIDMigration, "migrate_data"), @@ -281,6 +286,11 @@ def _get_states_index_names(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventIDPostMigration, "migrate_data"), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(migration, "post_migrate_entity_ids", return_value=False), @@ -407,6 +417,11 @@ def _get_states_index_names(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventIDPostMigration, "migrate_data"), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(migration, "post_migrate_entity_ids", return_value=False), @@ -546,6 +561,11 @@ def _get_states_index_names(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventIDPostMigration, "migrate_data"), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(migration, "post_migrate_entity_ids", return_value=False), @@ -730,6 +750,11 @@ def _get_states_index_names(): with ( patch.object(recorder, "db_schema", old_db_schema), patch.object(migration, "SCHEMA_VERSION", old_db_schema.SCHEMA_VERSION), + patch.object( + migration, + "LIVE_MIGRATION_MIN_SCHEMA_VERSION", + get_patched_live_version(old_db_schema), + ), patch.object(migration.EventIDPostMigration, "migrate_data"), patch.object(migration, "non_live_data_migration_needed", return_value=False), patch.object(migration, "post_migrate_entity_ids", return_value=False), diff --git a/tests/components/tuya/__init__.py b/tests/components/tuya/__init__.py index 5ef914ee0b11b1..62a5fd4fd3ae02 100644 --- a/tests/components/tuya/__init__.py +++ b/tests/components/tuya/__init__.py @@ -15,6 +15,7 @@ DEVICE_MOCKS = [ "bzyd_45idzfufidgee7ir", # https://github.com/orgs/home-assistant/discussions/717 "bzyd_ssimhf6r8kgwepfb", # https://github.com/orgs/home-assistant/discussions/718 + "cjkg_uenof8jd", # https://github.com/home-assistant/core/issues/151825 "ckmkzq_1yyqfw4djv9eii3q", # https://github.com/home-assistant/core/issues/150856 "cl_3r8gc33pnqsxfe1g", # https://github.com/tuya/tuya-home-assistant/issues/754 "cl_669wsr2w4cvinbh4", # https://github.com/home-assistant/core/issues/150856 @@ -144,6 +145,7 @@ "hps_2aaelwxk", # https://github.com/home-assistant/core/issues/149704 "hps_wqashyqo", # https://github.com/home-assistant/core/issues/146180 "hwsb_ircs2n82vgrozoew", # https://github.com/home-assistant/core/issues/149233 + "jsq_r492ifwk6f2ssptb", # https://github.com/home-assistant/core/issues/151488 "jtmspro_xqeob8h6", # https://github.com/orgs/home-assistant/discussions/517 "kg_4nqs33emdwJxpQ8O", # https://github.com/orgs/home-assistant/discussions/539 "kg_5ftkaulg", # https://github.com/orgs/home-assistant/discussions/539 diff --git a/tests/components/tuya/fixtures/cjkg_uenof8jd.json b/tests/components/tuya/fixtures/cjkg_uenof8jd.json new file mode 100644 index 00000000000000..b7fbef51226046 --- /dev/null +++ b/tests/components/tuya/fixtures/cjkg_uenof8jd.json @@ -0,0 +1,171 @@ +{ + "endpoint": "https://apigw.tuyaus.com", + "mqtt_connected": true, + "disabled_by": null, + "disabled_polling": false, + "name": "Smart Switch", + "category": "cjkg", + "product_id": "uenof8jd", + "product_name": "Smart Switch", + "online": false, + "sub": true, + "time_zone": "+02:00", + "active_time": "2025-09-05T21:27:51+00:00", + "create_time": "2025-09-05T21:27:51+00:00", + "update_time": "2025-09-05T21:27:51+00:00", + "function": { + "scene_1": { + "type": "Enum", + "value": { + "range": ["scene"] + } + }, + "scene_2": { + "type": "Enum", + "value": { + "range": ["scene"] + } + }, + "mode_1": { + "type": "Enum", + "value": { + "range": ["switch_1", "scene_1"] + } + }, + "mode_2": { + "type": "Enum", + "value": { + "range": ["switch_2", "scene_2"] + } + }, + "switch_1": { + "type": "Boolean", + "value": {} + }, + "switch_2": { + "type": "Boolean", + "value": {} + }, + "countdown_1": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 43200, + "scale": 0, + "step": 1 + } + }, + "countdown_2": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 43200, + "scale": 0, + "step": 1 + } + }, + "switch_backlight": { + "type": "Boolean", + "value": {} + }, + "light_mode": { + "type": "Enum", + "value": { + "range": ["none", "relay", "pos"] + } + }, + "relay_status": { + "type": "Enum", + "value": { + "range": ["power_off", "power_on", "last"] + } + } + }, + "status_range": { + "scene_1": { + "type": "Enum", + "value": { + "range": ["scene"] + } + }, + "scene_2": { + "type": "Enum", + "value": { + "range": ["scene"] + } + }, + "mode_1": { + "type": "Enum", + "value": { + "range": ["switch_1", "scene_1"] + } + }, + "mode_2": { + "type": "Enum", + "value": { + "range": ["switch_2", "scene_2"] + } + }, + "switch_1": { + "type": "Boolean", + "value": {} + }, + "switch_2": { + "type": "Boolean", + "value": {} + }, + "countdown_1": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 43200, + "scale": 0, + "step": 1 + } + }, + "countdown_2": { + "type": "Integer", + "value": { + "unit": "s", + "min": 0, + "max": 43200, + "scale": 0, + "step": 1 + } + }, + "switch_backlight": { + "type": "Boolean", + "value": {} + }, + "light_mode": { + "type": "Enum", + "value": { + "range": ["none", "relay", "pos"] + } + }, + "relay_status": { + "type": "Enum", + "value": { + "range": ["power_off", "power_on", "last"] + } + } + }, + "status": { + "scene_1": "scene", + "scene_2": "scene", + "mode_1": "switch_1", + "mode_2": "switch_2", + "switch_1": false, + "switch_2": false, + "countdown_1": 0, + "countdown_2": 0, + "switch_backlight": true, + "light_mode": "pos", + "relay_status": "power_off" + }, + "set_up": false, + "support_local": true +} diff --git a/tests/components/tuya/fixtures/jsq_r492ifwk6f2ssptb.json b/tests/components/tuya/fixtures/jsq_r492ifwk6f2ssptb.json new file mode 100644 index 00000000000000..ed8595e8655226 --- /dev/null +++ b/tests/components/tuya/fixtures/jsq_r492ifwk6f2ssptb.json @@ -0,0 +1,86 @@ +{ + "endpoint": "https://apigw.tuyaeu.com", + "mqtt_connected": true, + "disabled_by": null, + "disabled_polling": false, + "name": "KLARTA HUMEA", + "category": "jsq", + "product_id": "r492ifwk6f2ssptb", + "product_name": "KLARTA HUMEA", + "online": false, + "sub": false, + "time_zone": "+02:00", + "active_time": "2024-09-03T09:32:08+00:00", + "create_time": "2024-09-03T09:32:08+00:00", + "update_time": "2024-09-03T09:32:08+00:00", + "function": { + "switch": { + "type": "Boolean", + "value": {} + }, + "sleep": { + "type": "Boolean", + "value": {} + }, + "level": { + "type": "Enum", + "value": { + "range": [ + "level_1", + "level_2", + "level_3", + "level_4", + "level_5", + "level_6", + "level_7", + "level_8", + "level_9" + ] + } + } + }, + "status_range": { + "switch": { + "type": "Boolean", + "value": {} + }, + "sleep": { + "type": "Boolean", + "value": {} + }, + "humidity_current": { + "type": "Integer", + "value": { + "unit": "", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "level": { + "type": "Enum", + "value": { + "range": [ + "level_1", + "level_2", + "level_3", + "level_4", + "level_5", + "level_6", + "level_7", + "level_8", + "level_9" + ] + } + } + }, + "status": { + "switch": false, + "sleep": false, + "level": "level_1", + "humidity_current": 76 + }, + "set_up": true, + "support_local": true +} diff --git a/tests/components/tuya/snapshots/test_humidifier.ambr b/tests/components/tuya/snapshots/test_humidifier.ambr index 5343b73e5e7e88..631e1983e07bfa 100644 --- a/tests/components/tuya/snapshots/test_humidifier.ambr +++ b/tests/components/tuya/snapshots/test_humidifier.ambr @@ -111,6 +111,61 @@ 'state': 'on', }) # --- +# name: test_platform_setup_and_discovery[humidifier.klarta_humea-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max_humidity': 100, + 'min_humidity': 0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'humidifier', + 'entity_category': None, + 'entity_id': 'humidifier.klarta_humea', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'tuya.btpss2f6kwfi294rqsjswitch', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[humidifier.klarta_humea-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'humidifier', + 'friendly_name': 'KLARTA HUMEA', + 'max_humidity': 100, + 'min_humidity': 0, + 'supported_features': , + }), + 'context': , + 'entity_id': 'humidifier.klarta_humea', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- # name: test_platform_setup_and_discovery[humidifier.living_room_dehumidifier-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/tuya/snapshots/test_init.ambr b/tests/components/tuya/snapshots/test_init.ambr index 3bb6181d76f300..6ac4ed9d711189 100644 --- a/tests/components/tuya/snapshots/test_init.ambr +++ b/tests/components/tuya/snapshots/test_init.ambr @@ -2231,6 +2231,37 @@ 'via_device_id': None, }) # --- +# name: test_device_registry[btpss2f6kwfi294rqsj] + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'tuya', + 'btpss2f6kwfi294rqsj', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'Tuya', + 'model': 'KLARTA HUMEA', + 'model_id': 'r492ifwk6f2ssptb', + 'name': 'KLARTA HUMEA', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }) +# --- # name: test_device_registry[btyk53n3v10z7a97zc] DeviceRegistryEntrySnapshot({ 'area_id': None, @@ -2665,6 +2696,37 @@ 'via_device_id': None, }) # --- +# name: test_device_registry[dj8foneugkjc] + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'tuya', + 'dj8foneugkjc', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'Tuya', + 'model': 'Smart Switch (unsupported)', + 'model_id': 'uenof8jd', + 'name': 'Smart Switch', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }) +# --- # name: test_device_registry[dke76hazlc] DeviceRegistryEntrySnapshot({ 'area_id': None, diff --git a/tests/components/tuya/snapshots/test_select.ambr b/tests/components/tuya/snapshots/test_select.ambr index 77b0c55340c134..81c28ae0b03a48 100644 --- a/tests/components/tuya/snapshots/test_select.ambr +++ b/tests/components/tuya/snapshots/test_select.ambr @@ -3018,6 +3018,77 @@ 'state': 'forward', }) # --- +# name: test_platform_setup_and_discovery[select.klarta_humea_spraying_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'level_1', + 'level_2', + 'level_3', + 'level_4', + 'level_5', + 'level_6', + 'level_7', + 'level_8', + 'level_9', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.klarta_humea_spraying_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Spraying level', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'humidifier_level', + 'unique_id': 'tuya.btpss2f6kwfi294rqsjlevel', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[select.klarta_humea_spraying_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'KLARTA HUMEA Spraying level', + 'options': list([ + 'level_1', + 'level_2', + 'level_3', + 'level_4', + 'level_5', + 'level_6', + 'level_7', + 'level_8', + 'level_9', + ]), + }), + 'context': , + 'entity_id': 'select.klarta_humea_spraying_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- # name: test_platform_setup_and_discovery[select.lave_linge_indicator_light_mode-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/tuya/snapshots/test_sensor.ambr b/tests/components/tuya/snapshots/test_sensor.ambr index 53caaf34216d77..d71642619a7af1 100644 --- a/tests/components/tuya/snapshots/test_sensor.ambr +++ b/tests/components/tuya/snapshots/test_sensor.ambr @@ -2201,6 +2201,174 @@ 'state': 'high', }) # --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_dew_point-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_dew_point', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Dew point', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dew_point_temperature', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqdew_point_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_dew_point-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Dew point', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_dew_point', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-40.0', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_feels_like-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_feels_like', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Feels like', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'feels_like_temperature', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqfeellike_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_feels_like-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Feels like', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_feels_like', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-65.0', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_heat_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_heat_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Heat index', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'heat_index_temperature', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqheat_index', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_heat_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Heat index', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_heat_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '26.0', + }) +# --- # name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2963,6 +3131,62 @@ 'state': '0.0', }) # --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_chill_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_chill_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wind chill index', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'wind_chill_index_temperature', + 'unique_id': 'tuya.6tbtkuv3tal1aesfjxqwindchill_index', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_chill_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'BR 7-in-1 WLAN Wetterstation Anthrazit Wind chill index', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_chill_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-65.0', + }) +# --- # name: test_platform_setup_and_discovery[sensor.br_7_in_1_wlan_wetterstation_anthrazit_wind_direction-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -9785,6 +10009,58 @@ 'state': 'high', }) # --- +# name: test_platform_setup_and_discovery[sensor.klarta_humea_humidity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.klarta_humea_humidity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Humidity', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'humidity', + 'unique_id': 'tuya.btpss2f6kwfi294rqsjhumidity_current', + 'unit_of_measurement': '', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.klarta_humea_humidity-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'KLARTA HUMEA Humidity', + 'state_class': , + 'unit_of_measurement': '', + }), + 'context': , + 'entity_id': 'sensor.klarta_humea_humidity', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- # name: test_platform_setup_and_discovery[sensor.lave_linge_current-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -16913,6 +17189,174 @@ 'state': '1007.8', }) # --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_dew_point-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.sws_16600_wifi_sh_dew_point', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Dew point', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dew_point_temperature', + 'unique_id': 'tuya.ai9swgb6tyinbwbxjxqdew_point_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_dew_point-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'SWS 16600 WiFi SH Dew point', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.sws_16600_wifi_sh_dew_point', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '14.5', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_feels_like-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.sws_16600_wifi_sh_feels_like', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Feels like', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'feels_like_temperature', + 'unique_id': 'tuya.ai9swgb6tyinbwbxjxqfeellike_temp', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_feels_like-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'SWS 16600 WiFi SH Feels like', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.sws_16600_wifi_sh_feels_like', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20.7', + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_heat_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.sws_16600_wifi_sh_heat_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Heat index', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'heat_index_temperature', + 'unique_id': 'tuya.ai9swgb6tyinbwbxjxqheat_index', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_heat_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'SWS 16600 WiFi SH Heat index', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.sws_16600_wifi_sh_heat_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '50.1', + }) +# --- # name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -17675,6 +18119,62 @@ 'state': '0.0', }) # --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_wind_chill_index-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.sws_16600_wifi_sh_wind_chill_index', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Wind chill index', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'wind_chill_index_temperature', + 'unique_id': 'tuya.ai9swgb6tyinbwbxjxqwindchill_index', + 'unit_of_measurement': , + }) +# --- +# name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_wind_chill_index-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'SWS 16600 WiFi SH Wind chill index', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.sws_16600_wifi_sh_wind_chill_index', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20.5', + }) +# --- # name: test_platform_setup_and_discovery[sensor.sws_16600_wifi_sh_wind_speed-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/tuya/snapshots/test_switch.ambr b/tests/components/tuya/snapshots/test_switch.ambr index 07c223cd615229..1b30c6cabea44e 100644 --- a/tests/components/tuya/snapshots/test_switch.ambr +++ b/tests/components/tuya/snapshots/test_switch.ambr @@ -5417,6 +5417,54 @@ 'state': 'off', }) # --- +# name: test_platform_setup_and_discovery[switch.klarta_humea_sleep-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': , + 'entity_id': 'switch.klarta_humea_sleep', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Sleep', + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'sleep', + 'unique_id': 'tuya.btpss2f6kwfi294rqsjsleep', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[switch.klarta_humea_sleep-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'KLARTA HUMEA Sleep', + }), + 'context': , + 'entity_id': 'switch.klarta_humea_sleep', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- # name: test_platform_setup_and_discovery[switch.lave_linge_child_lock-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py index 92fbca483fd158..3fd7e52522502b 100644 --- a/tests/components/vacuum/test_init.py +++ b/tests/components/vacuum/test_init.py @@ -53,25 +53,6 @@ def test_all(module: ModuleType) -> None: help_test_all(module) -@pytest.mark.parametrize( - ("enum", "constant_prefix"), _create_tuples(vacuum.VacuumEntityFeature, "SUPPORT_") -) -@pytest.mark.parametrize( - "module", - [vacuum], -) -def test_deprecated_constants( - caplog: pytest.LogCaptureFixture, - enum: Enum, - constant_prefix: str, - module: ModuleType, -) -> None: - """Test deprecated constants.""" - import_and_test_deprecated_constant_enum( - caplog, module, enum, constant_prefix, "2025.10" - ) - - @pytest.mark.parametrize( ("enum", "constant_prefix"), _create_tuples(vacuum.VacuumActivity, "STATE_") )