From ce5f2330eb31453d066f581ffeeaedbc101d6b03 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 7 May 2026 11:28:11 +0200 Subject: [PATCH] Read Tuya device info from quirk (#169888) --- homeassistant/components/tuya/util.py | 17 +++- tests/components/tuya/test_init.py | 109 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tuya/util.py b/homeassistant/components/tuya/util.py index 9743b8a1e64..6fcb7e67600 100644 --- a/homeassistant/components/tuya/util.py +++ b/homeassistant/components/tuya/util.py @@ -1,5 +1,6 @@ """Utility methods for the Tuya integration.""" +from tuya_device_handlers import TUYA_QUIRKS_REGISTRY from tuya_sharing import CustomerDevice from homeassistant.exceptions import ServiceValidationError @@ -36,7 +37,9 @@ class ActionDPCodeNotFoundError(ServiceValidationError): def get_device_info(device: CustomerDevice, *, initial: bool = False) -> DeviceInfo: """Get device info.""" - model = device.product_name + manufacturer = "Tuya" + model: str | None = device.product_name + model_id: str | None = device.product_id if initial: # Note: the model is overridden via entity.device_info property @@ -44,10 +47,18 @@ def get_device_info(device: CustomerDevice, *, initial: bool = False) -> DeviceI # stay as unsupported model = f"{device.product_name} (unsupported)" + if ( + quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device) + ) and quirk.manufacturer: + # If the manufacturer is not set, we cannot trust the model/model_id + manufacturer = quirk.manufacturer + model = quirk.model + model_id = quirk.model_id + return DeviceInfo( identifiers={(DOMAIN, device.id)}, - manufacturer="Tuya", + manufacturer=manufacturer, name=device.name, model=model, - model_id=device.product_id, + model_id=model_id, ) diff --git a/tests/components/tuya/test_init.py b/tests/components/tuya/test_init.py index 8ac17cff597..c11bbd0ebc1 100644 --- a/tests/components/tuya/test_init.py +++ b/tests/components/tuya/test_init.py @@ -2,6 +2,7 @@ from unittest.mock import MagicMock, patch +import pytest from syrupy.assertion import SnapshotAssertion from tuya_device_handlers import TUYA_QUIRKS_REGISTRY from tuya_sharing import CustomerDevice, Manager @@ -14,6 +15,7 @@ from homeassistant.components.tuya.const import ( DOMAIN, ) from homeassistant.components.tuya.diagnostics import _REDACTED_DPCODES +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -141,6 +143,113 @@ async def test_device_registry( ) +@pytest.mark.parametrize( + ("mock_device_code", "platforms", "manufacturer", "model", "model_id", "quirks"), + [ + # Ensure model is suffixed with "(unsupported)" when no entities + # are generated + ( + "mal_gyitctrjj1kefxp2", + [], + "Tuya", + "Multifunction alarm (unsupported)", + "gyitctrjj1kefxp2", + {}, + ), + # Ensure model is not suffixed with "(unsupported)" when entities + # are generated + ( + "mal_gyitctrjj1kefxp2", + [Platform.ALARM_CONTROL_PANEL], + "Tuya", + "Multifunction alarm", + "gyitctrjj1kefxp2", + {}, + ), + # With a quirk that has manufacturer, model and model_id are + # taken from quirk (and not suffixed with "(unsupported)" even if + # no entities are generated) + ( + "mal_gyitctrjj1kefxp2", + [], + "My manufacturer", + "Amazing model", + "AMA-ZING1", + { + "gyitctrjj1kefxp2": MagicMock( + manufacturer="My manufacturer", + model="Amazing model", + model_id="AMA-ZING1", + ) + }, + ), + # With a quirk that has manufacturer, model and model_id are + # taken from quirk (even if None) + ( + "mal_gyitctrjj1kefxp2", + [], + "My manufacturer", + None, + None, + { + "gyitctrjj1kefxp2": MagicMock( + manufacturer="My manufacturer", + model=None, + model_id=None, + ) + }, + ), + # With a quirk that has null manufacturer, model and model_id + # are ignored + ( + "mal_gyitctrjj1kefxp2", + [], + "Tuya", + "Multifunction alarm (unsupported)", + "gyitctrjj1kefxp2", + { + "gyitctrjj1kefxp2": MagicMock( + manufacturer=None, + model="Amazing model", + model_id="AMA-ZING1", + ) + }, + ), + ], +) +async def test_device_registry_with_quirk( + hass: HomeAssistant, + mock_manager: Manager, + mock_config_entry: MockConfigEntry, + mock_device: CustomerDevice, + device_registry: dr.DeviceRegistry, + platforms: list[Platform], + manufacturer: str, + model: str | None, + model_id: str | None, + quirks: dict[str, MagicMock], +) -> None: + """Validate device information with and without quirks.""" + + with ( + patch.dict(TUYA_QUIRKS_REGISTRY._quirks, quirks, clear=True), + patch("homeassistant.components.tuya.coordinator.register_tuya_quirks"), + patch("homeassistant.components.tuya.PLATFORMS", platforms), + ): + await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) + + device_registry_entries = dr.async_entries_for_config_entry( + device_registry, mock_config_entry.entry_id + ) + assert len(device_registry_entries) == 1 + device_registry_entry = device_registry_entries[0] + + assert device_registry_entry.manufacturer == manufacturer + assert device_registry_entry.model == model + assert device_registry_entry.model_id == model_id + assert device_registry_entry.name == "Multifunction alarm" + + @patch.object( TUYA_QUIRKS_REGISTRY, "initialise_device_quirk",