From 3f74cca253f6ee5b070433299cdd6f2a6a717df2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 31 Mar 2026 09:48:49 +0000 Subject: [PATCH] Add ability to load custom Tuya quirks --- homeassistant/components/tuya/__init__.py | 2 ++ homeassistant/components/tuya/camera.py | 18 ++++++++++++++++++ homeassistant/components/tuya/fan.py | 18 ++++++++++++++++++ homeassistant/components/tuya/vacuum.py | 20 ++++++++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tuya/__init__.py b/homeassistant/components/tuya/__init__.py index 0555f8a145a..81e7b330e58 100644 --- a/homeassistant/components/tuya/__init__.py +++ b/homeassistant/components/tuya/__init__.py @@ -5,6 +5,7 @@ from __future__ import annotations import logging from typing import Any, NamedTuple +from tuya_device_handlers.devices import register_tuya_quirks from tuya_sharing import ( CustomerDevice, Manager, @@ -107,6 +108,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TuyaConfigEntry) -> bool model_id=device.product_id, ) + await hass.async_add_executor_job(register_tuya_quirks, "/config/tuya_quirks/") await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # If the device does not register any entities, the device does not need to subscribe # So the subscription is here diff --git a/homeassistant/components/tuya/camera.py b/homeassistant/components/tuya/camera.py index 36b69885b2e..92b1e90bc5d 100644 --- a/homeassistant/components/tuya/camera.py +++ b/homeassistant/components/tuya/camera.py @@ -2,6 +2,7 @@ from __future__ import annotations +from tuya_device_handlers import TUYA_QUIRKS_REGISTRY from tuya_device_handlers.definition.camera import ( TuyaCameraDefinition, get_default_definition, @@ -28,6 +29,20 @@ CAMERAS: dict[DeviceCategory, CameraEntityDescription] = { } +def _get_quirk_entities( + manager: Manager, device: CustomerDevice +) -> list[TuyaCameraEntity] | None: + if (quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device)) is None or ( + entity_quirks := quirk.camera_quirks + ) is None: + return None + return [ + TuyaCameraEntity(device, manager, definition) + for entity_quirk in entity_quirks + if (definition := entity_quirk.definition_fn(device)) + ] + + async def async_setup_entry( hass: HomeAssistant, entry: TuyaConfigEntry, @@ -42,6 +57,9 @@ async def async_setup_entry( entities: list[TuyaCameraEntity] = [] for device_id in device_ids: device = manager.device_map[device_id] + if (quirk_entities := _get_quirk_entities(manager, device)) is not None: + entities.extend(quirk_entities) + continue if description := CAMERAS.get(device.category): entities.append( TuyaCameraEntity( diff --git a/homeassistant/components/tuya/fan.py b/homeassistant/components/tuya/fan.py index 5d0db0adc67..9f8a4f48e26 100644 --- a/homeassistant/components/tuya/fan.py +++ b/homeassistant/components/tuya/fan.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any +from tuya_device_handlers import TUYA_QUIRKS_REGISTRY from tuya_device_handlers.definition.fan import ( TuyaFanDefinition, get_default_definition, @@ -44,6 +45,20 @@ _HA_TO_TUYA_DIRECTION_MAPPINGS = { } +def _get_quirk_entities( + manager: Manager, device: CustomerDevice +) -> list[TuyaFanEntity] | None: + if (quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device)) is None or ( + entity_quirks := quirk.fan_quirks + ) is None: + return None + return [ + TuyaFanEntity(device, manager, definition) + for entity_quirk in entity_quirks + if (definition := entity_quirk.definition_fn(device)) + ] + + async def async_setup_entry( hass: HomeAssistant, entry: TuyaConfigEntry, @@ -58,6 +73,9 @@ async def async_setup_entry( entities: list[TuyaFanEntity] = [] for device_id in device_ids: device = manager.device_map[device_id] + if (quirk_entities := _get_quirk_entities(manager, device)) is not None: + entities.extend(quirk_entities) + continue if (description := FANS.get(device.category)) and ( definition := get_default_definition(device) ): diff --git a/homeassistant/components/tuya/vacuum.py b/homeassistant/components/tuya/vacuum.py index ef2eba4a5fa..a541191092d 100644 --- a/homeassistant/components/tuya/vacuum.py +++ b/homeassistant/components/tuya/vacuum.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any +from tuya_device_handlers import TUYA_QUIRKS_REGISTRY from tuya_device_handlers.definition.vacuum import ( TuyaVacuumDefinition, get_default_definition, @@ -42,6 +43,20 @@ VACUUMS: dict[DeviceCategory, StateVacuumEntityDescription] = { } +def _get_quirk_entities( + manager: Manager, device: CustomerDevice +) -> list[TuyaVacuumEntity] | None: + if (quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device)) is None or ( + entity_quirks := quirk.vacuum_quirks + ) is None: + return None + return [ + TuyaVacuumEntity(device, manager, definition) + for entity_quirk in entity_quirks + if (definition := entity_quirk.definition_fn(device)) + ] + + async def async_setup_entry( hass: HomeAssistant, entry: TuyaConfigEntry, @@ -56,9 +71,10 @@ async def async_setup_entry( entities: list[TuyaVacuumEntity] = [] for device_id in device_ids: device = manager.device_map[device_id] + if (quirk_entities := _get_quirk_entities(manager, device)) is not None: + entities.extend(quirk_entities) + continue if description := VACUUMS.get(device.category): - entities.append( - TuyaVacuumEntity( device, manager, description, get_default_definition(device) ) )