Преглед изворни кода

Add Gateway subdevices integration.

Nicolas пре 2 година
родитељ
комит
f5a1570b3a

+ 4 - 4
custom_components/tuya_local/__init__.py

@@ -22,7 +22,7 @@ from .const import (
     CONF_TYPE,
     DOMAIN,
 )
-from .device import setup_device, async_delete_device
+from .device import setup_device, get_device_id, async_delete_device
 from .helpers.device_config import get_config
 
 _LOGGER = logging.getLogger(__name__)
@@ -230,7 +230,7 @@ async def async_migrate_entry(hass, entry: ConfigEntry):
 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
     _LOGGER.debug(
         "Setting up entry for device: %s",
-        entry.data[CONF_DEVICE_ID],
+        get_device_id(entry.data),
     )
     config = {**entry.data, **entry.options, "name": entry.title}
     setup_device(hass, config)
@@ -255,7 +255,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
     _LOGGER.debug("Unloading entry for device: %s", entry.data[CONF_DEVICE_ID])
     config = entry.data
-    data = hass.data[DOMAIN][config[CONF_DEVICE_ID]]
+    data = hass.data[DOMAIN][get_device_id(config)]
     device_conf = get_config(config[CONF_TYPE])
     if device_conf is None:
         _LOGGER.error(NOT_FOUND, config[CONF_TYPE])
@@ -273,7 +273,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
         await hass.config_entries.async_forward_entry_unload(entry, e)
 
     await async_delete_device(hass, config)
-    del hass.data[DOMAIN][config[CONF_DEVICE_ID]]
+    del hass.data[DOMAIN][get_device_id(config)]
 
     return True
 

+ 9 - 1
custom_components/tuya_local/config_flow.py

@@ -13,9 +13,11 @@ from .const import (
     CONF_DEVICE_ID,
     CONF_LOCAL_KEY,
     CONF_POLL_ONLY,
+    CONF_DEVICE_CID,
     CONF_PROTOCOL_VERSION,
     CONF_TYPE,
 )
+from .helpers.config import get_device_id
 from .helpers.device_config import get_config
 from .helpers.log import log_json
 
@@ -35,9 +37,10 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
         key_opts = {}
         proto_opts = {"default": "auto"}
         polling_opts = {"default": False}
+        devcid_opts = {}
 
         if user_input is not None:
-            await self.async_set_unique_id(user_input[CONF_DEVICE_ID])
+            await self.async_set_unique_id(get_device_id(user_input))
             self._abort_if_unique_id_configured()
 
             self.device = await async_test_connection(user_input, self.hass)
@@ -49,6 +52,8 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
                 devid_opts["default"] = user_input[CONF_DEVICE_ID]
                 host_opts["default"] = user_input[CONF_HOST]
                 key_opts["default"] = user_input[CONF_LOCAL_KEY]
+                if CONF_DEVICE_CID in user_input:
+                    devcid_opts["default"] = user_input[CONF_DEVICE_CID]
                 proto_opts["default"] = user_input[CONF_PROTOCOL_VERSION]
                 polling_opts["default"] = user_input[CONF_POLL_ONLY]
 
@@ -64,6 +69,7 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
                         **proto_opts,
                     ): vol.In(["auto"] + API_PROTOCOL_VERSIONS),
                     vol.Required(CONF_POLL_ONLY, **polling_opts): bool,
+                    vol.Optional(CONF_DEVICE_CID, **devcid_opts): str,
                 }
             ),
             errors=errors,
@@ -188,12 +194,14 @@ async def async_test_connection(config: dict, hass: HomeAssistant):
         await asyncio.sleep(5)
 
     try:
+        subdevice_id = config[CONF_DEVICE_CID] if CONF_DEVICE_CID in config else None
         device = TuyaLocalDevice(
             "Test",
             config[CONF_DEVICE_ID],
             config[CONF_HOST],
             config[CONF_LOCAL_KEY],
             config[CONF_PROTOCOL_VERSION],
+            subdevice_id,
             hass,
             True,
         )

+ 1 - 0
custom_components/tuya_local/const.py

@@ -6,5 +6,6 @@ CONF_DEVICE_ID = "device_id"
 CONF_LOCAL_KEY = "local_key"
 CONF_TYPE = "type"
 CONF_POLL_ONLY = "poll_only"
+CONF_DEVICE_CID = "device_cid"
 CONF_PROTOCOL_VERSION = "protocol_version"
 API_PROTOCOL_VERSIONS = [3.3, 3.1, 3.2, 3.4, 3.5]

+ 18 - 8
custom_components/tuya_local/device.py

@@ -24,7 +24,9 @@ from .const import (
     CONF_POLL_ONLY,
     CONF_PROTOCOL_VERSION,
     DOMAIN,
+    CONF_DEVICE_CID
 )
+from .helpers.config import get_device_id
 from .helpers.device_config import possible_matches
 from .helpers.log import log_json
 
@@ -40,6 +42,7 @@ class TuyaLocalDevice(object):
         address,
         local_key,
         protocol_version,
+        dev_cid,
         hass: HomeAssistant,
         poll_only=False,
     ):
@@ -51,6 +54,7 @@ class TuyaLocalDevice(object):
             address (str): The network address.
             local_key (str): The encryption key.
             protocol_version (str | number): The protocol version.
+            dev_cid (str): The sub device id.
             hass (HomeAssistant): The Home Assistant instance.
             poll_only (bool): True if the device should be polled only
         """
@@ -63,7 +67,11 @@ class TuyaLocalDevice(object):
         self._api_protocol_version_index = None
         self._api_protocol_working = False
         try:
-            self._api = tinytuya.Device(dev_id, address, local_key)
+            if dev_cid is not None:
+                self._api = tinytuya.Device(dev_id, cid=dev_cid, parent=tinytuya.Device(dev_id, address, local_key, version=protocol_version))
+            else:
+                self._api = tinytuya.Device(dev_id, address, local_key, version=protocol_version)
+            self.dev_cid = dev_cid
         except Exception as e:
             _LOGGER.error(
                 "%s: %s while initialising device %s",
@@ -104,8 +112,8 @@ class TuyaLocalDevice(object):
 
     @property
     def unique_id(self):
-        """Return the unique id for this device (the dev_id)."""
-        return self._api.id
+        """Return the unique id for this device (the dev_id or dev_cid)."""
+        return self.dev_cid if self.dev_cid is not None else self._api.id
 
     @property
     def device_info(self):
@@ -570,7 +578,7 @@ class TuyaLocalDevice(object):
 def setup_device(hass: HomeAssistant, config: dict):
     """Setup a tuya device based on passed in config."""
 
-    _LOGGER.info("Creating device: %s", config[CONF_DEVICE_ID])
+    _LOGGER.info("Creating device: %s", get_device_id(config))
     hass.data[DOMAIN] = hass.data.get(DOMAIN, {})
     device = TuyaLocalDevice(
         config[CONF_NAME],
@@ -578,15 +586,17 @@ def setup_device(hass: HomeAssistant, config: dict):
         config[CONF_HOST],
         config[CONF_LOCAL_KEY],
         config[CONF_PROTOCOL_VERSION],
+        config[CONF_DEVICE_CID] if CONF_DEVICE_CID in config else None,
         hass,
         config[CONF_POLL_ONLY],
     )
-    hass.data[DOMAIN][config[CONF_DEVICE_ID]] = {"device": device}
+    hass.data[DOMAIN][get_device_id(config)] = {"device": device}
 
     return device
 
 
 async def async_delete_device(hass: HomeAssistant, config: dict):
-    _LOGGER.info("Deleting device: %s", config[CONF_DEVICE_ID])
-    await hass.data[DOMAIN][config[CONF_DEVICE_ID]]["device"].async_stop()
-    del hass.data[DOMAIN][config[CONF_DEVICE_ID]]["device"]
+    device_id = get_device_id(config)
+    _LOGGER.info("Deleting device: %s", device_id)
+    await hass.data[DOMAIN][device_id]["device"].async_stop()
+    del hass.data[DOMAIN][device_id]["device"]

+ 6 - 2
custom_components/tuya_local/helpers/config.py

@@ -4,7 +4,7 @@ Helper for general config
 import logging
 
 from .. import DOMAIN
-from ..const import CONF_DEVICE_ID, CONF_TYPE
+from ..const import CONF_DEVICE_ID, CONF_DEVICE_CID, CONF_TYPE
 from .device_config import get_config
 
 _LOGGER = logging.getLogger(__name__)
@@ -14,7 +14,7 @@ async def async_tuya_setup_platform(
     hass, async_add_entities, discovery_info, platform, entity_class
 ):
     """Common functions for async_setup_platform for each entity platform."""
-    data = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    data = hass.data[DOMAIN][get_device_id(discovery_info)]
     device = data["device"]
     entities = []
 
@@ -43,3 +43,7 @@ async def async_tuya_setup_platform(
     if not entities:
         raise ValueError(f"{device.name} does not support use as a {platform} device.")
     async_add_entities(entities)
+
+
+def get_device_id(config: dict):
+    return config[CONF_DEVICE_CID] if CONF_DEVICE_CID in config and config[CONF_DEVICE_CID] != "" else config[CONF_DEVICE_ID]

+ 4 - 2
custom_components/tuya_local/translations/en.json

@@ -10,7 +10,8 @@
                     "device_id": "Device ID (uuid)",
                     "local_key": "Local key",
                     "protocol_version": "Protocol version (try auto if not known)",
-                    "poll_only": "Poll only (try this if your device does not work fully)"
+                    "poll_only": "Poll only (try this if your device does not work fully)",
+                    "device_cid": "Sub device ID (for devices connected via gateway)"
                 }
             },
             "select_type": {
@@ -45,7 +46,8 @@
                     "host": "IP address or hostname",
                     "local_key": "Local key",
                     "protocol_version": "Protocol version (try auto if not known)",
-                    "poll_only": "Poll only (try this if your device does not work fully)"
+                    "poll_only": "Poll only (try this if your device does not work fully)",
+                    "device_cid": "Sub device ID (for devices connected via gateway)"
                 }
             }
         },