mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Refactor Tuya device handling (#57545)
* Refactor Tuya device handling * Tweak
This commit is contained in:
parent
fb18c108d1
commit
8ec38ef034
@ -1,7 +1,8 @@
|
|||||||
"""Support for Tuya Smart devices."""
|
"""Support for Tuya Smart devices."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
from tuya_iot import (
|
from tuya_iot import (
|
||||||
AuthType,
|
AuthType,
|
||||||
@ -30,24 +31,25 @@ from .const import (
|
|||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
TUYA_DEVICE_MANAGER,
|
|
||||||
TUYA_DISCOVERY_NEW,
|
TUYA_DISCOVERY_NEW,
|
||||||
TUYA_HA_DEVICES,
|
|
||||||
TUYA_HA_SIGNAL_UPDATE_ENTITY,
|
TUYA_HA_SIGNAL_UPDATE_ENTITY,
|
||||||
TUYA_HA_TUYA_MAP,
|
TUYA_SUPPORTED_PRODUCT_CATEGORIES,
|
||||||
TUYA_HOME_MANAGER,
|
|
||||||
TUYA_MQTT_LISTENER,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HomeAssistantTuyaData(NamedTuple):
|
||||||
|
"""Tuya data stored in the Home Assistant data object."""
|
||||||
|
|
||||||
|
device_listener: TuyaDeviceListener
|
||||||
|
device_manager: TuyaDeviceManager
|
||||||
|
home_manager: TuyaHomeManager
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Async setup hass config entry."""
|
"""Async setup hass config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
|
hass.data.setdefault(DOMAIN, {})
|
||||||
TUYA_HA_TUYA_MAP: {},
|
|
||||||
TUYA_HA_DEVICES: set(),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Project type has been renamed to auth type in the upstream Tuya IoT SDK.
|
# Project type has been renamed to auth type in the upstream Tuya IoT SDK.
|
||||||
# This migrates existing config entries to reflect that name change.
|
# This migrates existing config entries to reflect that name change.
|
||||||
@ -98,25 +100,28 @@ async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
tuya_mq = TuyaOpenMQ(api)
|
tuya_mq = TuyaOpenMQ(api)
|
||||||
tuya_mq.start()
|
tuya_mq.start()
|
||||||
|
|
||||||
|
device_ids: set[str] = set()
|
||||||
device_manager = TuyaDeviceManager(api, tuya_mq)
|
device_manager = TuyaDeviceManager(api, tuya_mq)
|
||||||
|
|
||||||
# Get device list
|
|
||||||
home_manager = TuyaHomeManager(api, tuya_mq, device_manager)
|
home_manager = TuyaHomeManager(api, tuya_mq, device_manager)
|
||||||
await hass.async_add_executor_job(home_manager.update_device_cache)
|
listener = DeviceListener(hass, device_manager, device_ids)
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HOME_MANAGER] = home_manager
|
|
||||||
|
|
||||||
listener = DeviceListener(hass, entry)
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_MQTT_LISTENER] = listener
|
|
||||||
device_manager.add_device_listener(listener)
|
device_manager.add_device_listener(listener)
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER] = device_manager
|
|
||||||
|
|
||||||
# Clean up device entities
|
hass.data[DOMAIN][entry.entry_id] = HomeAssistantTuyaData(
|
||||||
|
device_listener=listener,
|
||||||
|
device_manager=device_manager,
|
||||||
|
home_manager=home_manager,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get devices & clean up device entities
|
||||||
|
await hass.async_add_executor_job(home_manager.update_device_cache)
|
||||||
await cleanup_device_registry(hass, device_manager)
|
await cleanup_device_registry(hass, device_manager)
|
||||||
|
|
||||||
_LOGGER.debug("init support type->%s", PLATFORMS)
|
# Register known device IDs
|
||||||
|
for device in device_manager.device_map.values():
|
||||||
|
if device.category in TUYA_SUPPORTED_PRODUCT_CATEGORIES:
|
||||||
|
device_ids.add(device.id)
|
||||||
|
|
||||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -134,17 +139,13 @@ async def cleanup_device_registry(
|
|||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unloading the Tuya platforms."""
|
"""Unloading the Tuya platforms."""
|
||||||
_LOGGER.debug("integration unload")
|
|
||||||
unload = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
unload = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if unload:
|
if unload:
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
device_manager.mq.stop()
|
hass_data.device_manager.mq.stop()
|
||||||
device_manager.remove_device_listener(
|
hass_data.device_manager.remove_device_listener(hass_data.device_listener)
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_MQTT_LISTENER]
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
if not hass.data[DOMAIN]:
|
if not hass.data[DOMAIN]:
|
||||||
hass.data.pop(DOMAIN)
|
hass.data.pop(DOMAIN)
|
||||||
|
|
||||||
@ -154,14 +155,20 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
class DeviceListener(TuyaDeviceListener):
|
class DeviceListener(TuyaDeviceListener):
|
||||||
"""Device Update Listener."""
|
"""Device Update Listener."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_manager: TuyaDeviceManager,
|
||||||
|
device_ids: set[str],
|
||||||
|
) -> None:
|
||||||
"""Init DeviceListener."""
|
"""Init DeviceListener."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.entry = entry
|
self.device_manager = device_manager
|
||||||
|
self.device_ids = device_ids
|
||||||
|
|
||||||
def update_device(self, device: TuyaDevice) -> None:
|
def update_device(self, device: TuyaDevice) -> None:
|
||||||
"""Update device status."""
|
"""Update device status."""
|
||||||
if device.id in self.hass.data[DOMAIN][self.entry.entry_id][TUYA_HA_DEVICES]:
|
if device.id in self.device_ids:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"_update-->%s;->>%s",
|
"_update-->%s;->>%s",
|
||||||
self,
|
self,
|
||||||
@ -171,33 +178,14 @@ class DeviceListener(TuyaDeviceListener):
|
|||||||
|
|
||||||
def add_device(self, device: TuyaDevice) -> None:
|
def add_device(self, device: TuyaDevice) -> None:
|
||||||
"""Add device added listener."""
|
"""Add device added listener."""
|
||||||
device_add = False
|
if device.category in TUYA_SUPPORTED_PRODUCT_CATEGORIES:
|
||||||
|
# Ensure the device isn't present stale
|
||||||
if device.category in itertools.chain(
|
|
||||||
*self.hass.data[DOMAIN][self.entry.entry_id][TUYA_HA_TUYA_MAP].values()
|
|
||||||
):
|
|
||||||
ha_tuya_map = self.hass.data[DOMAIN][self.entry.entry_id][TUYA_HA_TUYA_MAP]
|
|
||||||
self.hass.add_job(self.async_remove_device, device.id)
|
self.hass.add_job(self.async_remove_device, device.id)
|
||||||
|
|
||||||
for domain, tuya_list in ha_tuya_map.items():
|
self.device_ids.add(device.id)
|
||||||
if device.category in tuya_list:
|
dispatcher_send(self.hass, TUYA_DISCOVERY_NEW, [device.id])
|
||||||
device_add = True
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Add device category->%s; domain-> %s",
|
|
||||||
device.category,
|
|
||||||
domain,
|
|
||||||
)
|
|
||||||
self.hass.data[DOMAIN][self.entry.entry_id][TUYA_HA_DEVICES].add(
|
|
||||||
device.id
|
|
||||||
)
|
|
||||||
dispatcher_send(
|
|
||||||
self.hass, TUYA_DISCOVERY_NEW.format(domain), [device.id]
|
|
||||||
)
|
|
||||||
|
|
||||||
if device_add:
|
device_manager = self.device_manager
|
||||||
device_manager = self.hass.data[DOMAIN][self.entry.entry_id][
|
|
||||||
TUYA_DEVICE_MANAGER
|
|
||||||
]
|
|
||||||
device_manager.mq.stop()
|
device_manager.mq.stop()
|
||||||
tuya_mq = TuyaOpenMQ(device_manager.api)
|
tuya_mq = TuyaOpenMQ(device_manager.api)
|
||||||
tuya_mq.start()
|
tuya_mq.start()
|
||||||
@ -207,18 +195,16 @@ class DeviceListener(TuyaDeviceListener):
|
|||||||
|
|
||||||
def remove_device(self, device_id: str) -> None:
|
def remove_device(self, device_id: str) -> None:
|
||||||
"""Add device removed listener."""
|
"""Add device removed listener."""
|
||||||
_LOGGER.debug("tuya remove device:%s", device_id)
|
|
||||||
self.hass.add_job(self.async_remove_device, device_id)
|
self.hass.add_job(self.async_remove_device, device_id)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_remove_device(self, device_id: str) -> None:
|
def async_remove_device(self, device_id: str) -> None:
|
||||||
"""Remove device from Home Assistant."""
|
"""Remove device from Home Assistant."""
|
||||||
|
_LOGGER.debug("Remove device: %s", device_id)
|
||||||
device_registry_object = device_registry.async_get(self.hass)
|
device_registry_object = device_registry.async_get(self.hass)
|
||||||
device_entry = device_registry_object.async_get_device(
|
device_entry = device_registry_object.async_get_device(
|
||||||
identifiers={(DOMAIN, device_id)}
|
identifiers={(DOMAIN, device_id)}
|
||||||
)
|
)
|
||||||
if device_entry is not None:
|
if device_entry is not None:
|
||||||
device_registry_object.async_remove_device(device_entry.id)
|
device_registry_object.async_remove_device(device_entry.id)
|
||||||
self.hass.data[DOMAIN][self.entry.entry_id][TUYA_HA_DEVICES].discard(
|
self.device_ids.discard(device_id)
|
||||||
device_id
|
|
||||||
)
|
|
||||||
|
@ -8,7 +8,7 @@ from typing import Any
|
|||||||
|
|
||||||
from tuya_iot import TuyaDevice, TuyaDeviceManager
|
from tuya_iot import TuyaDevice, TuyaDeviceManager
|
||||||
|
|
||||||
from homeassistant.components.climate import DOMAIN as DEVICE_DOMAIN, ClimateEntity
|
from homeassistant.components.climate import ClimateEntity
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
HVAC_MODE_AUTO,
|
HVAC_MODE_AUTO,
|
||||||
HVAC_MODE_COOL,
|
HVAC_MODE_COOL,
|
||||||
@ -25,17 +25,11 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import HomeAssistantTuyaData
|
||||||
from .base import TuyaHaEntity
|
from .base import TuyaHaEntity
|
||||||
from .const import (
|
from .const import DOMAIN, TUYA_DISCOVERY_NEW
|
||||||
DOMAIN,
|
|
||||||
TUYA_DEVICE_MANAGER,
|
|
||||||
TUYA_DISCOVERY_NEW,
|
|
||||||
TUYA_HA_DEVICES,
|
|
||||||
TUYA_HA_TUYA_MAP,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -88,50 +82,25 @@ TUYA_SUPPORT_TYPE = {
|
|||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up tuya climate dynamically through tuya discovery."""
|
"""Set up Tuya climate dynamically through Tuya discovery."""
|
||||||
_LOGGER.debug("climate init")
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_TUYA_MAP][
|
|
||||||
DEVICE_DOMAIN
|
|
||||||
] = TUYA_SUPPORT_TYPE
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_discover_device(dev_ids: list[str]) -> None:
|
def async_discover_device(device_ids: list[str]) -> None:
|
||||||
"""Discover and add a discovered tuya climate."""
|
"""Discover and add a discovered Tuya climate."""
|
||||||
_LOGGER.debug("climate add-> %s", dev_ids)
|
entities: list[TuyaHaClimate] = []
|
||||||
if not dev_ids:
|
for device_id in device_ids:
|
||||||
return
|
device = hass_data.device_manager.device_map[device_id]
|
||||||
entities = _setup_entities(hass, entry, dev_ids)
|
if device and device.category in TUYA_SUPPORT_TYPE:
|
||||||
|
entities.append(TuyaHaClimate(device, hass_data.device_manager))
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
async_discover_device([*hass_data.device_manager.device_map])
|
||||||
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
|
||||||
hass, TUYA_DISCOVERY_NEW.format(DEVICE_DOMAIN), async_discover_device
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
device_ids = []
|
|
||||||
for (device_id, device) in device_manager.device_map.items():
|
|
||||||
if device.category in TUYA_SUPPORT_TYPE:
|
|
||||||
device_ids.append(device_id)
|
|
||||||
async_discover_device(device_ids)
|
|
||||||
|
|
||||||
|
|
||||||
def _setup_entities(
|
|
||||||
hass: HomeAssistant, entry: ConfigEntry, device_ids: list[str]
|
|
||||||
) -> list[Entity]:
|
|
||||||
"""Set up Tuya Climate."""
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
entities: list[Entity] = []
|
|
||||||
for device_id in device_ids:
|
|
||||||
device = device_manager.device_map[device_id]
|
|
||||||
if device is None:
|
|
||||||
continue
|
|
||||||
entities.append(TuyaHaClimate(device, device_manager))
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_DEVICES].add(device_id)
|
|
||||||
return entities
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaHaClimate(TuyaHaEntity, ClimateEntity):
|
class TuyaHaClimate(TuyaHaEntity, ClimateEntity):
|
||||||
"""Tuya Switch Device."""
|
"""Tuya Switch Device."""
|
||||||
|
@ -15,12 +15,8 @@ CONF_PASSWORD = "password"
|
|||||||
CONF_COUNTRY_CODE = "country_code"
|
CONF_COUNTRY_CODE = "country_code"
|
||||||
CONF_APP_TYPE = "tuya_app_type"
|
CONF_APP_TYPE = "tuya_app_type"
|
||||||
|
|
||||||
TUYA_DISCOVERY_NEW = "tuya_discovery_new_{}"
|
TUYA_DISCOVERY_NEW = "tuya_discovery_new"
|
||||||
TUYA_DEVICE_MANAGER = "tuya_device_manager"
|
TUYA_HA_SIGNAL_UPDATE_ENTITY = "tuya_entry_update"
|
||||||
TUYA_HOME_MANAGER = "tuya_home_manager"
|
|
||||||
TUYA_MQTT_LISTENER = "tuya_mqtt_listener"
|
|
||||||
TUYA_HA_TUYA_MAP = "tuya_ha_tuya_map"
|
|
||||||
TUYA_HA_DEVICES = "tuya_ha_devices"
|
|
||||||
|
|
||||||
TUYA_RESPONSE_CODE = "code"
|
TUYA_RESPONSE_CODE = "code"
|
||||||
TUYA_RESPONSE_RESULT = "result"
|
TUYA_RESPONSE_RESULT = "result"
|
||||||
@ -28,7 +24,29 @@ TUYA_RESPONSE_MSG = "msg"
|
|||||||
TUYA_RESPONSE_SUCCESS = "success"
|
TUYA_RESPONSE_SUCCESS = "success"
|
||||||
TUYA_RESPONSE_PLATFROM_URL = "platform_url"
|
TUYA_RESPONSE_PLATFROM_URL = "platform_url"
|
||||||
|
|
||||||
TUYA_HA_SIGNAL_UPDATE_ENTITY = "tuya_entry_update"
|
TUYA_SUPPORTED_PRODUCT_CATEGORIES = (
|
||||||
|
"bh", # Smart Kettle
|
||||||
|
"cwysj", # Pet Water Feeder
|
||||||
|
"cz", # Socket
|
||||||
|
"dc", # Light string
|
||||||
|
"dd", # Light strip
|
||||||
|
"dj", # Light
|
||||||
|
"dlq", # Breaker
|
||||||
|
"fs", # Fan
|
||||||
|
"fs", # Fan
|
||||||
|
"fwl", # Ambient light
|
||||||
|
"jsq", # Humidifier's light
|
||||||
|
"kg", # Switch
|
||||||
|
"kj", # Air Purifier
|
||||||
|
"kj", # Air Purifier
|
||||||
|
"kt", # Air conditioner
|
||||||
|
"pc", # Power Strip
|
||||||
|
"qn", # Heater
|
||||||
|
"wk", # Thermostat
|
||||||
|
"xdd", # Ceiling Light
|
||||||
|
"xxj", # Diffuser
|
||||||
|
"xxj", # Diffuser's light
|
||||||
|
)
|
||||||
|
|
||||||
TUYA_SMART_APP = "tuyaSmart"
|
TUYA_SMART_APP = "tuyaSmart"
|
||||||
SMARTLIFE_APP = "smartlife"
|
SMARTLIFE_APP = "smartlife"
|
||||||
|
@ -10,7 +10,6 @@ from tuya_iot import TuyaDevice, TuyaDeviceManager
|
|||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
DIRECTION_FORWARD,
|
DIRECTION_FORWARD,
|
||||||
DIRECTION_REVERSE,
|
DIRECTION_REVERSE,
|
||||||
DOMAIN as DEVICE_DOMAIN,
|
|
||||||
SUPPORT_DIRECTION,
|
SUPPORT_DIRECTION,
|
||||||
SUPPORT_OSCILLATE,
|
SUPPORT_OSCILLATE,
|
||||||
SUPPORT_PRESET_MODE,
|
SUPPORT_PRESET_MODE,
|
||||||
@ -26,14 +25,9 @@ from homeassistant.util.percentage import (
|
|||||||
percentage_to_ordered_list_item,
|
percentage_to_ordered_list_item,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from . import HomeAssistantTuyaData
|
||||||
from .base import TuyaHaEntity
|
from .base import TuyaHaEntity
|
||||||
from .const import (
|
from .const import DOMAIN, TUYA_DISCOVERY_NEW
|
||||||
DOMAIN,
|
|
||||||
TUYA_DEVICE_MANAGER,
|
|
||||||
TUYA_DISCOVERY_NEW,
|
|
||||||
TUYA_HA_DEVICES,
|
|
||||||
TUYA_HA_TUYA_MAP,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -61,49 +55,24 @@ async def async_setup_entry(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
):
|
):
|
||||||
"""Set up tuya fan dynamically through tuya discovery."""
|
"""Set up tuya fan dynamically through tuya discovery."""
|
||||||
_LOGGER.debug("fan init")
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_TUYA_MAP][
|
|
||||||
DEVICE_DOMAIN
|
|
||||||
] = TUYA_SUPPORT_TYPE
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_discover_device(dev_ids: list[str]) -> None:
|
def async_discover_device(device_ids: list[str]) -> None:
|
||||||
"""Discover and add a discovered tuya fan."""
|
"""Discover and add a discovered tuya fan."""
|
||||||
_LOGGER.debug("fan add-> %s", dev_ids)
|
entities: list[TuyaHaFan] = []
|
||||||
if not dev_ids:
|
for device_id in device_ids:
|
||||||
return
|
device = hass_data.device_manager.device_map[device_id]
|
||||||
entities = _setup_entities(hass, entry, dev_ids)
|
if device and device.category in TUYA_SUPPORT_TYPE:
|
||||||
|
entities.append(TuyaHaFan(device, hass_data.device_manager))
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
async_discover_device([*hass_data.device_manager.device_map])
|
||||||
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
|
||||||
hass, TUYA_DISCOVERY_NEW.format(DEVICE_DOMAIN), async_discover_device
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
device_ids = []
|
|
||||||
for (device_id, device) in device_manager.device_map.items():
|
|
||||||
if device.category in TUYA_SUPPORT_TYPE:
|
|
||||||
device_ids.append(device_id)
|
|
||||||
async_discover_device(device_ids)
|
|
||||||
|
|
||||||
|
|
||||||
def _setup_entities(
|
|
||||||
hass: HomeAssistant, entry: ConfigEntry, device_ids: list[str]
|
|
||||||
) -> list[TuyaHaFan]:
|
|
||||||
"""Set up Tuya Fan."""
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
entities = []
|
|
||||||
for device_id in device_ids:
|
|
||||||
device = device_manager.device_map[device_id]
|
|
||||||
if device is None:
|
|
||||||
continue
|
|
||||||
entities.append(TuyaHaFan(device, device_manager))
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_DEVICES].add(device_id)
|
|
||||||
return entities
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaHaFan(TuyaHaEntity, FanEntity):
|
class TuyaHaFan(TuyaHaEntity, FanEntity):
|
||||||
"""Tuya Fan Device."""
|
"""Tuya Fan Device."""
|
||||||
|
@ -15,7 +15,6 @@ from homeassistant.components.light import (
|
|||||||
COLOR_MODE_COLOR_TEMP,
|
COLOR_MODE_COLOR_TEMP,
|
||||||
COLOR_MODE_HS,
|
COLOR_MODE_HS,
|
||||||
COLOR_MODE_ONOFF,
|
COLOR_MODE_ONOFF,
|
||||||
DOMAIN as DEVICE_DOMAIN,
|
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -23,14 +22,9 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import HomeAssistantTuyaData
|
||||||
from .base import TuyaHaEntity
|
from .base import TuyaHaEntity
|
||||||
from .const import (
|
from .const import DOMAIN, TUYA_DISCOVERY_NEW
|
||||||
DOMAIN,
|
|
||||||
TUYA_DEVICE_MANAGER,
|
|
||||||
TUYA_DISCOVERY_NEW,
|
|
||||||
TUYA_HA_DEVICES,
|
|
||||||
TUYA_HA_TUYA_MAP,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -84,54 +78,24 @@ async def async_setup_entry(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up tuya light dynamically through tuya discovery."""
|
"""Set up tuya light dynamically through tuya discovery."""
|
||||||
_LOGGER.debug("light init")
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_TUYA_MAP][
|
|
||||||
DEVICE_DOMAIN
|
|
||||||
] = TUYA_SUPPORT_TYPE
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_discover_device(dev_ids: list[str]):
|
def async_discover_device(device_ids: list[str]):
|
||||||
"""Discover and add a discovered tuya light."""
|
"""Discover and add a discovered tuya light."""
|
||||||
_LOGGER.debug("light add-> %s", dev_ids)
|
entities: list[TuyaHaLight] = []
|
||||||
if not dev_ids:
|
for device_id in device_ids:
|
||||||
return
|
device = hass_data.device_manager.device_map[device_id]
|
||||||
entities = _setup_entities(hass, entry, dev_ids)
|
if device and device.category in TUYA_SUPPORT_TYPE:
|
||||||
|
entities.append(TuyaHaLight(device, hass_data.device_manager))
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
async_discover_device([*hass_data.device_manager.device_map])
|
||||||
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
|
||||||
hass, TUYA_DISCOVERY_NEW.format(DEVICE_DOMAIN), async_discover_device
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
device_ids = []
|
|
||||||
for (device_id, device) in device_manager.device_map.items():
|
|
||||||
if device.category in TUYA_SUPPORT_TYPE:
|
|
||||||
device_ids.append(device_id)
|
|
||||||
async_discover_device(device_ids)
|
|
||||||
|
|
||||||
|
|
||||||
def _setup_entities(
|
|
||||||
hass, entry: ConfigEntry, device_ids: list[str]
|
|
||||||
) -> list[TuyaHaLight]:
|
|
||||||
"""Set up Tuya Light device."""
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
entities = []
|
|
||||||
for device_id in device_ids:
|
|
||||||
device = device_manager.device_map[device_id]
|
|
||||||
if device is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
tuya_ha_light = TuyaHaLight(device, device_manager)
|
|
||||||
entities.append(tuya_ha_light)
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_DEVICES].add(
|
|
||||||
tuya_ha_light.tuya_device.id
|
|
||||||
)
|
|
||||||
|
|
||||||
return entities
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaHaLight(TuyaHaEntity, LightEntity):
|
class TuyaHaLight(TuyaHaEntity, LightEntity):
|
||||||
"""Tuya light device."""
|
"""Tuya light device."""
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Support for Tuya scenes."""
|
"""Support for Tuya scenes."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from tuya_iot import TuyaHomeManager, TuyaScene
|
from tuya_iot import TuyaHomeManager, TuyaScene
|
||||||
@ -9,55 +8,48 @@ from tuya_iot import TuyaHomeManager, TuyaScene
|
|||||||
from homeassistant.components.scene import Scene
|
from homeassistant.components.scene import Scene
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN, TUYA_HOME_MANAGER
|
from . import HomeAssistantTuyaData
|
||||||
|
from .const import DOMAIN
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up tuya scenes."""
|
"""Set up Tuya scenes."""
|
||||||
home_manager = hass.data[DOMAIN][entry.entry_id][TUYA_HOME_MANAGER]
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
scenes = await hass.async_add_executor_job(home_manager.query_scenes)
|
scenes = await hass.async_add_executor_job(hass_data.home_manager.query_scenes)
|
||||||
async_add_entities(TuyaHAScene(home_manager, scene) for scene in scenes)
|
async_add_entities(TuyaHAScene(hass_data.home_manager, scene) for scene in scenes)
|
||||||
|
|
||||||
|
|
||||||
class TuyaHAScene(Scene):
|
class TuyaHAScene(Scene):
|
||||||
"""Tuya Scene Remote."""
|
"""Tuya Scene Remote."""
|
||||||
|
|
||||||
|
_should_poll = False
|
||||||
|
|
||||||
def __init__(self, home_manager: TuyaHomeManager, scene: TuyaScene) -> None:
|
def __init__(self, home_manager: TuyaHomeManager, scene: TuyaScene) -> None:
|
||||||
"""Init Tuya Scene."""
|
"""Init Tuya Scene."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self._attr_unique_id = f"tys{scene.scene_id}"
|
||||||
self.home_manager = home_manager
|
self.home_manager = home_manager
|
||||||
self.scene = scene
|
self.scene = scene
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self) -> bool:
|
|
||||||
"""Hass should not poll."""
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str | None:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
return f"tys{self.scene.scene_id}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str | None:
|
def name(self) -> str | None:
|
||||||
"""Return Tuya scene name."""
|
"""Return Tuya scene name."""
|
||||||
return self.scene.name
|
return self.scene.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self) -> DeviceInfo:
|
||||||
"""Return a device description for device registry."""
|
"""Return a device description for device registry."""
|
||||||
return {
|
return DeviceInfo(
|
||||||
"identifiers": {(DOMAIN, f"{self.unique_id}")},
|
identifiers={(DOMAIN, f"{self.unique_id}")},
|
||||||
"manufacturer": "tuya",
|
manufacturer="tuya",
|
||||||
"name": self.scene.name,
|
name=self.scene.name,
|
||||||
"model": "Tuya Scene",
|
model="Tuya Scene",
|
||||||
}
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
|
@ -6,21 +6,15 @@ from typing import Any
|
|||||||
|
|
||||||
from tuya_iot import TuyaDevice, TuyaDeviceManager
|
from tuya_iot import TuyaDevice, TuyaDeviceManager
|
||||||
|
|
||||||
from homeassistant.components.switch import DOMAIN as DEVICE_DOMAIN, SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import HomeAssistantTuyaData
|
||||||
from .base import TuyaHaEntity
|
from .base import TuyaHaEntity
|
||||||
from .const import (
|
from .const import DOMAIN, TUYA_DISCOVERY_NEW
|
||||||
DOMAIN,
|
|
||||||
TUYA_DEVICE_MANAGER,
|
|
||||||
TUYA_DISCOVERY_NEW,
|
|
||||||
TUYA_HA_DEVICES,
|
|
||||||
TUYA_HA_TUYA_MAP,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -62,45 +56,36 @@ async def async_setup_entry(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up tuya sensors dynamically through tuya discovery."""
|
"""Set up tuya sensors dynamically through tuya discovery."""
|
||||||
_LOGGER.debug("switch init")
|
hass_data: HomeAssistantTuyaData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_TUYA_MAP][
|
@callback
|
||||||
DEVICE_DOMAIN
|
def async_discover_device(device_ids: list[str]) -> None:
|
||||||
] = TUYA_SUPPORT_TYPE
|
|
||||||
|
|
||||||
async def async_discover_device(dev_ids):
|
|
||||||
"""Discover and add a discovered tuya sensor."""
|
"""Discover and add a discovered tuya sensor."""
|
||||||
_LOGGER.debug("switch add-> %s", dev_ids)
|
async_add_entities(
|
||||||
if not dev_ids:
|
_setup_entities(hass, entry, hass_data.device_manager, device_ids)
|
||||||
return
|
)
|
||||||
entities = _setup_entities(hass, entry, dev_ids)
|
|
||||||
async_add_entities(entities)
|
async_discover_device([*hass_data.device_manager.device_map])
|
||||||
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
|
||||||
hass, TUYA_DISCOVERY_NEW.format(DEVICE_DOMAIN), async_discover_device
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
|
||||||
device_ids = []
|
|
||||||
for (device_id, device) in device_manager.device_map.items():
|
|
||||||
if device.category in TUYA_SUPPORT_TYPE:
|
|
||||||
device_ids.append(device_id)
|
|
||||||
await async_discover_device(device_ids)
|
|
||||||
|
|
||||||
|
def _setup_entities(
|
||||||
def _setup_entities(hass, entry: ConfigEntry, device_ids: list[str]) -> list[Entity]:
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
device_manager: TuyaDeviceManager,
|
||||||
|
device_ids: list[str],
|
||||||
|
) -> list[TuyaHaSwitch]:
|
||||||
"""Set up Tuya Switch device."""
|
"""Set up Tuya Switch device."""
|
||||||
device_manager = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER]
|
entities: list[TuyaHaSwitch] = []
|
||||||
entities: list[Entity] = []
|
|
||||||
for device_id in device_ids:
|
for device_id in device_ids:
|
||||||
device = device_manager.device_map[device_id]
|
device = device_manager.device_map[device_id]
|
||||||
if device is None:
|
if device is None or device.category not in TUYA_SUPPORT_TYPE:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for function in device.function:
|
for function in device.function:
|
||||||
tuya_ha_switch = None
|
|
||||||
if device.category == "kj":
|
if device.category == "kj":
|
||||||
if function in [
|
if function in [
|
||||||
DPCODE_ANION,
|
DPCODE_ANION,
|
||||||
@ -110,26 +95,26 @@ def _setup_entities(hass, entry: ConfigEntry, device_ids: list[str]) -> list[Ent
|
|||||||
DPCODE_UV,
|
DPCODE_UV,
|
||||||
DPCODE_WET,
|
DPCODE_WET,
|
||||||
]:
|
]:
|
||||||
tuya_ha_switch = TuyaHaSwitch(device, device_manager, function)
|
entities.append(TuyaHaSwitch(device, device_manager, function))
|
||||||
# Main device switch is handled by the Fan object
|
|
||||||
elif device.category == "cwysj":
|
elif device.category == "cwysj":
|
||||||
if function in [DPCODE_FRESET, DPCODE_UV, DPCODE_PRESET, DPCODE_WRESET]:
|
if (
|
||||||
tuya_ha_switch = TuyaHaSwitch(device, device_manager, function)
|
function
|
||||||
|
in [
|
||||||
|
DPCODE_FRESET,
|
||||||
|
DPCODE_UV,
|
||||||
|
DPCODE_PRESET,
|
||||||
|
DPCODE_WRESET,
|
||||||
|
]
|
||||||
|
or function.startswith(DPCODE_SWITCH)
|
||||||
|
):
|
||||||
|
entities.append(TuyaHaSwitch(device, device_manager, function))
|
||||||
|
|
||||||
if function.startswith(DPCODE_SWITCH):
|
elif function.startswith(DPCODE_START) or function.startswith(
|
||||||
# Main device switch
|
DPCODE_SWITCH
|
||||||
tuya_ha_switch = TuyaHaSwitch(device, device_manager, function)
|
):
|
||||||
else:
|
entities.append(TuyaHaSwitch(device, device_manager, function))
|
||||||
if function.startswith(DPCODE_START):
|
|
||||||
tuya_ha_switch = TuyaHaSwitch(device, device_manager, function)
|
|
||||||
if function.startswith(DPCODE_SWITCH):
|
|
||||||
tuya_ha_switch = TuyaHaSwitch(device, device_manager, function)
|
|
||||||
|
|
||||||
if tuya_ha_switch is not None:
|
|
||||||
entities.append(tuya_ha_switch)
|
|
||||||
hass.data[DOMAIN][entry.entry_id][TUYA_HA_DEVICES].add(
|
|
||||||
tuya_ha_switch.tuya_device.id
|
|
||||||
)
|
|
||||||
return entities
|
return entities
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user