async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry_data = entry_decrypt(hass, entry, entry.data) project_type = ProjectType(entry_data[CONF_PROJECT_TYPE]) api = TuyaOpenAPI( entry_data[CONF_ENDPOINT], entry_data[CONF_ACCESS_ID], entry_data[CONF_ACCESS_SECRET], project_type, ) api.set_dev_channel("hass") if project_type == ProjectType.INDUSTY_SOLUTIONS: response = await hass.async_add_executor_job(api.login, entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD]) else: response = await hass.async_add_executor_job( api.login, entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD], entry_data[CONF_COUNTRY_CODE], entry_data[CONF_APP_TYPE], ) if response.get("success", False) is False: _LOGGER.error("Tuya login error response: %s", response) return False tuya_mq = TuyaOpenMQ(api) tuya_mq.start() device_manager = TuyaDeviceManager(api, tuya_mq) # Get device list home_manager = TuyaHomeManager(api, tuya_mq, device_manager) await hass.async_add_executor_job(home_manager.update_device_cache) 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) hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE_MANAGER] = device_manager # Clean up device entities await cleanup_device_registry(hass, entry) _LOGGER.debug("init support type->%s", PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
def _init_tuya_sdk(hass: HomeAssistant, entry_data: dict) -> TuyaDeviceManager: api = TuyaOpenAPI(entry_data[CONF_ENDPOINT], entry_data[CONF_ACCESS_ID], entry_data[CONF_ACCESS_SECRET]) api.set_dev_channel('hass') response = api.login(entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD]) if response.get('success', False) == False: _LOGGER.error( "Tuya login error response: %s", response, ) return False mq = TuyaOpenMQ(api) mq.start() # Get device list devIds = [] assetManager = TuyaAssetManager(api) response = assetManager.getAssetList() assets = response.get('result', {}).get('assets', []) for asset in assets: asset_id = asset['asset_id'] devIds += assetManager.getDeviceList(asset_id) # Update device status deviceManager = TuyaDeviceManager(api, mq) deviceManager.updateDeviceCaches(devIds) # Create ha devices haDevices = [] for (devId, device) in deviceManager.deviceMap.items(): haDevice = crete_tuya_ha_device(device, deviceManager) if haDevice: haDevices.append(haDevice) hass.data[DOMAIN] = { 'haDevices': haDevices, } # Set mqtt listener def _onMessage(msg): devId = msg.get('data', {}).get('devId', '') haDevices = hass.data[DOMAIN]['haDevices'] for haDevice in haDevices: if haDevice.tuyaDevice.id == devId: haDevice.schedule_update_ha_state() mq.add_message_listener(_onMessage) return True
async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool: auth_type = AuthType(entry.data[CONF_AUTH_TYPE]) api = TuyaOpenAPI( endpoint=entry.data[CONF_ENDPOINT], access_id=entry.data[CONF_ACCESS_ID], access_secret=entry.data[CONF_ACCESS_SECRET], auth_type=auth_type, ) api.set_dev_channel("hass") if auth_type == AuthType.CUSTOM: response = await hass.async_add_executor_job( api.connect, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD] ) else: response = await hass.async_add_executor_job( api.connect, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], entry.data[CONF_COUNTRY_CODE], entry.data[CONF_APP_TYPE], ) if response.get("success", False) is False: _LOGGER.error("Tuya login error response: %s", response) return False tuya_mq = TuyaOpenMQ(api) tuya_mq.start() device_ids: set[str] = set() device_manager = TuyaDeviceManager(api, tuya_mq) home_manager = TuyaHomeManager(api, tuya_mq, device_manager) listener = DeviceListener(hass, device_manager, device_ids) device_manager.add_device_listener(listener) 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) # Migrate old unique_ids to the new format async_migrate_entities_unique_ids(hass, entry, device_manager) # Register known device IDs device_registry = dr.async_get(hass) for device in device_manager.device_map.values(): device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, device.id)}, manufacturer="Tuya", name=device.name, model=f"{device.product_name} (unsupported)", ) device_ids.add(device.id) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def _init_tuya_sdk(self) -> bool: auth_type = AuthType(0) api = TuyaOpenAPI( self._get_region_url(self._t_region), self._t_apikey, self._t_apisecret, auth_type, ) api.set_dev_channel("fhempy") response = ( await utils.run_blocking( functools.partial(api.connect, self._t_username, self._t_password) ) if auth_type == AuthType.CUSTOM else await utils.run_blocking( functools.partial( api.connect, self._t_username, self._t_password, self._get_countrycode(self._t_region), self._t_apptype, ) ) ) if response.get("success", False) is False: if response.get("code", 0) == 2406: await fhem.readingsSingleUpdate( self.hash, "state", "Tuya project too old, create new one", 1 ) else: await fhem.readingsSingleUpdate( self.hash, "state", f"failed to login: {response}", 1 ) self.logger.error(f"Tuya login error response: {response}") return False self.tuya_pulsar = TuyaOpenPulsar( self._t_apikey, self._t_apisecret, self._get_pulsar_endpoint(self._t_region), TuyaCloudPulsarTopic.PROD, ) self.tuya_pulsar.start() # check if pulsar is working await asyncio.sleep(2) pulsar_connected = False try: pulsar_connected = self.tuya_pulsar.ws_app.sock.status == 101 except Exception: pass if pulsar_connected is False: self.logger.error( "Please activate OpenPulsar: " + "https://developer.tuya.com/en/docs/iot/subscribe-mq?id=Kavqcrvckbh9h" ) # pulsar not working self.tuya_pulsar.stop() else: self.logger.info("Tuya Open Pulsar connected") self.tuya_mq = TuyaOpenMQ(api) self.tuya_mq.start() self.device_manager = TuyaDeviceManager(api, self.tuya_mq) # Get device list self.home_manager = TuyaHomeManager(api, self.tuya_mq, self.device_manager) await utils.run_blocking( functools.partial(self.home_manager.update_device_cache) ) t_cloud_setup = self class DeviceListener(TuyaDeviceListener): """Device Update Listener.""" def __init__(self, logger) -> None: super().__init__() self.logger = logger def update_device(self, device: TuyaDevice): self.logger.debug(f"update_device received for {device.id}") for dev in t_cloud_setup.tuya_devices: if dev.id == device.id: try: asyncio.run_coroutine_threadsafe( dev.update(device), t_cloud_setup.fhemdev.loop ) except Exception: self.logger.exception("Failed to update device") def add_device(self, device: TuyaDevice): self.logger.info(f"add_device received for {device.id}") try: asyncio.run_coroutine_threadsafe( self.add_fhem_device(device), t_cloud_setup.fhemdev.loop ) except Exception: self.logger.exception("Failed to add device") async def add_fhem_device(self, device: TuyaDevice): await t_cloud_setup._create_fhem_device(device.name, device.id) try: self.tuya_mq.stop() except Exception: pass self.tuya_mq = TuyaOpenMQ( t_cloud_setup.device_manager.device_manager.api ) self.tuya_mq.start() t_cloud_setup.device_manager.mq = self.tuya_mq self.tuya_mq.add_message_listener( t_cloud_setup.device_manager.on_message ) def remove_device(self, device_id: str): self.logger.info(f"remove_device received for {device_id}") __listener = DeviceListener(self.logger) self.device_manager.add_device_listener(__listener) def on_pulsar_message(msg): msg = json.loads(msg) status = msg.get("status", []) device = {"id": msg["devId"], "status": status} self.logger.debug(f"update_device received: {msg}") for dev in t_cloud_setup.tuya_devices: if dev.id == device["id"]: try: asyncio.run_coroutine_threadsafe( dev.update_readings_arr(device["status"]), t_cloud_setup.fhemdev.loop, ) except Exception: self.logger.exception("Failed to update device") self.tuya_pulsar.add_message_listener(on_pulsar_message) return True
class tuya_cloud_setup: def __init__(self, logger, fhemdevice: FhemModule): self.logger = logger self.fhemdev = fhemdevice self.hash = fhemdevice.hash self._t_devicelist = [] self._ready = False async def Define(self, hash, args, argsh): self._t_apikey = args[4] self._t_apisecret = args[5] self._t_username = args[6] self._t_password = args[7] if len(args) > 8: self._t_apptype = args[8] else: self._t_apptype = "smartlife" if len(args) > 9: self._t_region = args[9] else: self._t_region = "Europe" self.fhemdev.create_async_task(self.run_setup()) def _get_region_url(self, region) -> str: for url in TUYA_ENDPOINT: if TUYA_ENDPOINT[url] == region: return url def _get_pulsar_endpoint(self, region): return "wss://mqe.tuya" + self._get_countrycode(self._t_region) + ".com:8285/" def _get_countrycode(self, region) -> str: if region == "Europe": return "eu" else: return "cn" async def run_setup(self): await fhem.readingsSingleUpdate(self.hash, "state", "connecting", 1) if await self._init_tuya_sdk() is True: self._ready = True await fhem.readingsSingleUpdate(self.hash, "state", "connected", 1) await self._init_devices() async def _init_tuya_sdk(self) -> bool: auth_type = AuthType(0) api = TuyaOpenAPI( self._get_region_url(self._t_region), self._t_apikey, self._t_apisecret, auth_type, ) api.set_dev_channel("fhempy") response = ( await utils.run_blocking( functools.partial(api.connect, self._t_username, self._t_password) ) if auth_type == AuthType.CUSTOM else await utils.run_blocking( functools.partial( api.connect, self._t_username, self._t_password, self._get_countrycode(self._t_region), self._t_apptype, ) ) ) if response.get("success", False) is False: if response.get("code", 0) == 2406: await fhem.readingsSingleUpdate( self.hash, "state", "Tuya project too old, create new one", 1 ) else: await fhem.readingsSingleUpdate( self.hash, "state", f"failed to login: {response}", 1 ) self.logger.error(f"Tuya login error response: {response}") return False self.tuya_pulsar = TuyaOpenPulsar( self._t_apikey, self._t_apisecret, self._get_pulsar_endpoint(self._t_region), TuyaCloudPulsarTopic.PROD, ) self.tuya_pulsar.start() # check if pulsar is working await asyncio.sleep(2) pulsar_connected = False try: pulsar_connected = self.tuya_pulsar.ws_app.sock.status == 101 except Exception: pass if pulsar_connected is False: self.logger.error( "Please activate OpenPulsar: " + "https://developer.tuya.com/en/docs/iot/subscribe-mq?id=Kavqcrvckbh9h" ) # pulsar not working self.tuya_pulsar.stop() else: self.logger.info("Tuya Open Pulsar connected") self.tuya_mq = TuyaOpenMQ(api) self.tuya_mq.start() self.device_manager = TuyaDeviceManager(api, self.tuya_mq) # Get device list self.home_manager = TuyaHomeManager(api, self.tuya_mq, self.device_manager) await utils.run_blocking( functools.partial(self.home_manager.update_device_cache) ) t_cloud_setup = self class DeviceListener(TuyaDeviceListener): """Device Update Listener.""" def __init__(self, logger) -> None: super().__init__() self.logger = logger def update_device(self, device: TuyaDevice): self.logger.debug(f"update_device received for {device.id}") for dev in t_cloud_setup.tuya_devices: if dev.id == device.id: try: asyncio.run_coroutine_threadsafe( dev.update(device), t_cloud_setup.fhemdev.loop ) except Exception: self.logger.exception("Failed to update device") def add_device(self, device: TuyaDevice): self.logger.info(f"add_device received for {device.id}") try: asyncio.run_coroutine_threadsafe( self.add_fhem_device(device), t_cloud_setup.fhemdev.loop ) except Exception: self.logger.exception("Failed to add device") async def add_fhem_device(self, device: TuyaDevice): await t_cloud_setup._create_fhem_device(device.name, device.id) try: self.tuya_mq.stop() except Exception: pass self.tuya_mq = TuyaOpenMQ( t_cloud_setup.device_manager.device_manager.api ) self.tuya_mq.start() t_cloud_setup.device_manager.mq = self.tuya_mq self.tuya_mq.add_message_listener( t_cloud_setup.device_manager.on_message ) def remove_device(self, device_id: str): self.logger.info(f"remove_device received for {device_id}") __listener = DeviceListener(self.logger) self.device_manager.add_device_listener(__listener) def on_pulsar_message(msg): msg = json.loads(msg) status = msg.get("status", []) device = {"id": msg["devId"], "status": status} self.logger.debug(f"update_device received: {msg}") for dev in t_cloud_setup.tuya_devices: if dev.id == device["id"]: try: asyncio.run_coroutine_threadsafe( dev.update_readings_arr(device["status"]), t_cloud_setup.fhemdev.loop, ) except Exception: self.logger.exception("Failed to update device") self.tuya_pulsar.add_message_listener(on_pulsar_message) return True @property def ready(self): return self._ready def register_tuya_device(self, device): self._t_devicelist.append(device) @property def tuya_devices(self): return self._t_devicelist async def _create_fhem_device(self, name, device_id): devalias = name devname = name + "_" + device_id devname = utils.remove_umlaut(devname.replace(" ", "_").replace("-", "_")) device_exists = await fhem.checkIfDeviceExists( self.hash, "PYTHONTYPE", "tuya_cloud", "DEVICEID", device_id ) if not device_exists: self.logger.info( ( f"create: {devname} PythonModule tuya_cloud " f"{self.hash['NAME']} {device_id}" ) ) # define each device (CommandDefine ... tuya_cloud_setup_dev deviceid await fhem.CommandDefine( self.hash, ( f"{devname} PythonModule tuya_cloud " f"{self.hash['NAME']} {device_id}" ), ) await fhem.CommandAttr(self.hash, f"{devname} alias {devalias}") # wait for FHEM to handle CommandDefine await asyncio.sleep(1) async def _init_devices(self): # wait for init to complete, otherwise devices might not be available yet while await fhem.init_done(self.hash) == 0: await asyncio.sleep(3) # retrieve devices from device_manager and create them for device_id in self.device_manager.device_map: await self._create_fhem_device( self.device_manager.device_map[device_id].name, device_id ) async def send_commands(self, deviceid, commands): await utils.run_blocking( functools.partial(self.device_manager.send_commands, deviceid, commands) )
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Async setup hass config entry.""" hass.data.setdefault(DOMAIN, {}) # Project type has been renamed to auth type in the upstream Tuya IoT SDK. # This migrates existing config entries to reflect that name change. if CONF_PROJECT_TYPE in entry.data: data = {**entry.data, CONF_AUTH_TYPE: entry.data[CONF_PROJECT_TYPE]} data.pop(CONF_PROJECT_TYPE) hass.config_entries.async_update_entry(entry, data=data) auth_type = AuthType(entry.data[CONF_AUTH_TYPE]) api = TuyaOpenAPI( endpoint=entry.data[CONF_ENDPOINT], access_id=entry.data[CONF_ACCESS_ID], access_secret=entry.data[CONF_ACCESS_SECRET], auth_type=auth_type, ) api.set_dev_channel("hass") try: if auth_type == AuthType.CUSTOM: response = await hass.async_add_executor_job( api.connect, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD]) else: response = await hass.async_add_executor_job( api.connect, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], entry.data[CONF_COUNTRY_CODE], entry.data[CONF_APP_TYPE], ) except requests.exceptions.RequestException as err: raise ConfigEntryNotReady(err) from err if response.get("success", False) is False: raise ConfigEntryNotReady(response) tuya_mq = TuyaOpenMQ(api) tuya_mq.start() device_ids: set[str] = set() device_manager = TuyaDeviceManager(api, tuya_mq) home_manager = TuyaHomeManager(api, tuya_mq, device_manager) listener = DeviceListener(hass, device_manager, device_ids) device_manager.add_device_listener(listener) 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) # Migrate old unique_ids to the new format async_migrate_entities_unique_ids(hass, entry, device_manager) # Register known device IDs device_registry = dr.async_get(hass) for device in device_manager.device_map.values(): device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, device.id)}, manufacturer="Tuya", name=device.name, model=f"{device.product_name} (unsupported)", ) device_ids.add(device.id) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True
async def _init_tuya_sdk(hass: HomeAssistant, entry: ConfigEntry) -> bool: init_entry_data = entry.data # decrypt or encrypt entry info entry_data = entry_decrypt(hass, entry, init_entry_data) project_type = ProjectType(entry_data[CONF_PROJECT_TYPE]) api = TuyaOpenAPI( entry_data[CONF_ENDPOINT], entry_data[CONF_ACCESS_ID], entry_data[CONF_ACCESS_SECRET], project_type, ) api.set_dev_channel("hass") response = ( await hass.async_add_executor_job( api.login, entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD] ) if project_type == ProjectType.INDUSTY_SOLUTIONS else await hass.async_add_executor_job( api.login, entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD], entry_data[CONF_COUNTRY_CODE], entry_data[CONF_APP_TYPE], ) ) if response.get("success", False) is False: _LOGGER.error(f"Tuya login error response: {response}") return False tuya_mq = TuyaOpenMQ(api) tuya_mq.start() device_manager = TuyaDeviceManager(api, tuya_mq) # Get device list home_manager = TuyaHomeManager(api, tuya_mq, device_manager) await hass.async_add_executor_job(home_manager.update_device_cache) hass.data[DOMAIN][TUYA_HOME_MANAGER] = home_manager class DeviceListener(TuyaDeviceListener): """Device Update Listener.""" def update_device(self, device: TuyaDevice): for ha_device in hass.data[DOMAIN][TUYA_HA_DEVICES]: if ha_device.tuya_device.id == device.id: _LOGGER.debug(f"_update-->{self};->>{ha_device.tuya_device.status}") ha_device.schedule_update_ha_state() def add_device(self, device: TuyaDevice): device_add = False _LOGGER.info( f"""add device category->{device.category}; keys->, {hass.data[DOMAIN][TUYA_HA_TUYA_MAP].keys()}""" ) if device.category in itertools.chain( *hass.data[DOMAIN][TUYA_HA_TUYA_MAP].values() ): ha_tuya_map = hass.data[DOMAIN][TUYA_HA_TUYA_MAP] remove_hass_device(hass, device.id) for key, tuya_list in ha_tuya_map.items(): if device.category in tuya_list: device_add = True async_dispatcher_send( hass, TUYA_DISCOVERY_NEW.format(key), [device.id] ) if device_add: device_manager = hass.data[DOMAIN][TUYA_DEVICE_MANAGER] device_manager.mq.stop() tuya_mq = TuyaOpenMQ(device_manager.api) tuya_mq.start() device_manager.mq = tuya_mq tuya_mq.add_message_listener(device_manager.on_message) def remove_device(self, device_id: str): _LOGGER.info(f"tuya remove device:{device_id}") remove_hass_device(hass, device_id) __listener = DeviceListener() hass.data[DOMAIN][TUYA_MQTT_LISTENER] = __listener device_manager.add_device_listener(__listener) hass.data[DOMAIN][TUYA_DEVICE_MANAGER] = device_manager # Clean up device entities await cleanup_device_registry(hass) _LOGGER.info(f"init support type->{TUYA_SUPPORT_HA_TYPE}") for platform in TUYA_SUPPORT_HA_TYPE: _LOGGER.info(f"tuya async platform-->{platform}") hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) hass.data[DOMAIN][TUYA_SETUP_PLATFORM].add(platform) return True
TUYA_LOGGER.setLevel(logging.DEBUG) # Init openapi = TuyaOpenAPI(ENDPOINT, ACCESS_ID, ACCESS_KEY, AuthType.CUSTOM) openapi.connect(USERNAME, PASSWORD) openmq = TuyaOpenMQ(openapi) openmq.start() print("device test-> ", openapi.token_info.uid) # Get device list # assetManager = TuyaAssetManager(openapi) # devIds = assetManager.getDeviceList(ASSET_ID) # Update device status deviceManager = TuyaDeviceManager(openapi, openmq) homeManager = TuyaHomeManager(openapi, openmq, deviceManager) homeManager.update_device_cache() # # deviceManager.updateDeviceCaches(devIds) # device = deviceManager.deviceMap.get(DEVICE_ID) class tuyaDeviceListener(TuyaDeviceListener): def update_device(self, device: TuyaDevice): print("_update-->", device) def add_device(self, device: TuyaDevice): print("_add-->", device) def remove_device(self, device_id: str):