mirror of
https://github.com/home-assistant/core.git
synced 2026-05-18 06:20:17 +01:00
Add ability to load custom Tuya quirks (#166952)
This commit is contained in:
@@ -3,8 +3,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, NamedTuple
|
||||
|
||||
from tuya_device_handlers.devices import register_tuya_quirks
|
||||
from tuya_sharing import (
|
||||
CustomerDevice,
|
||||
Manager,
|
||||
@@ -58,6 +60,10 @@ def _create_manager(entry: TuyaConfigEntry, token_listener: TokenListener) -> Ma
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: TuyaConfigEntry) -> bool:
|
||||
"""Async setup hass config entry."""
|
||||
await hass.async_add_executor_job(
|
||||
register_tuya_quirks, str(Path(hass.config.config_dir, "tuya_quirks"))
|
||||
)
|
||||
|
||||
token_listener = TokenListener(hass, entry)
|
||||
|
||||
# Move to executor as it makes blocking call to import_module
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from tuya_device_handlers import TUYA_QUIRKS_REGISTRY
|
||||
from tuya_device_handlers.definition.camera import (
|
||||
CameraQuirk,
|
||||
TuyaCameraDefinition,
|
||||
get_default_definition,
|
||||
)
|
||||
@@ -28,6 +30,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, quirk=entity_quirk)
|
||||
for entity_quirk in entity_quirks
|
||||
if (definition := entity_quirk.definition_fn(device))
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: TuyaConfigEntry,
|
||||
@@ -42,10 +58,13 @@ 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(
|
||||
device, manager, description, get_default_definition(device)
|
||||
device, manager, get_default_definition(device), description
|
||||
)
|
||||
)
|
||||
|
||||
@@ -69,8 +88,10 @@ class TuyaCameraEntity(TuyaEntity, CameraEntity):
|
||||
self,
|
||||
device: CustomerDevice,
|
||||
device_manager: Manager,
|
||||
description: CameraEntityDescription,
|
||||
definition: TuyaCameraDefinition,
|
||||
description: CameraEntityDescription | None = None,
|
||||
*,
|
||||
quirk: CameraQuirk | None = None,
|
||||
) -> None:
|
||||
"""Init Tuya Camera."""
|
||||
super().__init__(device, device_manager, description)
|
||||
@@ -78,6 +99,8 @@ class TuyaCameraEntity(TuyaEntity, CameraEntity):
|
||||
self._attr_model = device.product_name
|
||||
self._motion_detection_switch = definition.motion_detection_switch
|
||||
self._recording_status = definition.recording_status
|
||||
if quirk and quirk.key:
|
||||
self._attr_unique_id = f"tuya.{device.id}_{quirk.key}"
|
||||
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
|
||||
@@ -24,11 +24,13 @@ class TuyaEntity(Entity):
|
||||
self,
|
||||
device: CustomerDevice,
|
||||
device_manager: Manager,
|
||||
description: EntityDescription,
|
||||
description: EntityDescription | None,
|
||||
) -> None:
|
||||
"""Init TuyaHaEntity."""
|
||||
self._attr_unique_id = f"tuya.{device.id}{description.key}"
|
||||
self.entity_description = description
|
||||
"""Init TuyaEntity."""
|
||||
self._attr_unique_id = f"tuya.{device.id}"
|
||||
if description:
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"tuya.{device.id}{description.key}"
|
||||
# TuyaEntity initialize mq can subscribe
|
||||
device.set_up = True
|
||||
self.device = device
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
from tuya_device_handlers import TUYA_QUIRKS_REGISTRY
|
||||
from tuya_device_handlers.definition.camera import CameraQuirk, get_default_definition
|
||||
from tuya_sharing import CustomerDevice, Manager
|
||||
|
||||
from homeassistant.components.camera import (
|
||||
@@ -37,16 +39,6 @@ def platform_autouse():
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_getrandbits():
|
||||
"""Mock camera access token which normally is randomized."""
|
||||
with patch(
|
||||
"homeassistant.components.camera.SystemRandom.getrandbits",
|
||||
return_value=1,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
async def test_platform_setup_and_discovery(
|
||||
hass: HomeAssistant,
|
||||
mock_manager: Manager,
|
||||
@@ -67,6 +59,34 @@ async def test_platform_setup_and_discovery(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mock_device_code", ["sp_rudejjigkywujjvs"])
|
||||
@pytest.mark.parametrize(
|
||||
("get_quirks", "available"),
|
||||
[
|
||||
(None, True),
|
||||
([], False),
|
||||
([CameraQuirk(key="", definition_fn=get_default_definition)], True),
|
||||
([CameraQuirk(key="", definition_fn=lambda d: None)], False),
|
||||
],
|
||||
)
|
||||
async def test_empty_quirk(
|
||||
hass: HomeAssistant,
|
||||
mock_manager: Manager,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_device: CustomerDevice,
|
||||
get_quirks: list | None,
|
||||
available: bool,
|
||||
) -> None:
|
||||
"""Test None quirks use defaults and empty quirk list skips default entities."""
|
||||
with patch.object(TUYA_QUIRKS_REGISTRY, "get_quirk_for_device") as mock_get_quirk:
|
||||
mock_get_quirk.return_value = Mock()
|
||||
mock_get_quirk.return_value.camera_quirks = get_quirks
|
||||
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
|
||||
|
||||
state = hass.states.get("camera.burocam")
|
||||
assert (state is not None) is available
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mock_device_code",
|
||||
["sp_rudejjigkywujjvs"],
|
||||
|
||||
Reference in New Issue
Block a user