""" Platform for Tuya WiFi-connected devices. Based on nikrolls/homeassistant-goldair-climate for Goldair branded devices. Based on sean6541/tuya-homeassistant for service call logic, and TarxBoy's investigation into Goldair's tuyapi statuses https://github.com/codetheweb/tuyapi/issues/31. """ import logging from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from .const import ( CONF_DEVICE_ID, CONF_LIGHT, CONF_LOCAL_KEY, CONF_LOCK, CONF_TYPE, DOMAIN, ) from .device import setup_device, delete_device from .helpers.device_config import get_config _LOGGER = logging.getLogger(__name__) async def async_migrate_entry(hass, entry: ConfigEntry): """Migrate to latest config format.""" CONF_TYPE_AUTO = "auto" CONF_DISPLAY_LIGHT = "display_light" CONF_CHILD_LOCK = "child_lock" if entry.version == 1: # Removal of Auto detection. config = {**entry.data, **entry.options, "name": entry.title} opts = {**entry.options} if config[CONF_TYPE] == CONF_TYPE_AUTO: device = setup_device(hass, config) config[CONF_TYPE] = await device.async_inferred_type() if config[CONF_TYPE] is None: return False entry.data = { CONF_DEVICE_ID: config[CONF_DEVICE_ID], CONF_LOCAL_KEY: config[CONF_LOCAL_KEY], CONF_HOST: config[CONF_HOST], } if CONF_CHILD_LOCK in config: opts.pop(CONF_CHILD_LOCK, False) opts[CONF_LOCK] = config[CONF_CHILD_LOCK] if CONF_DISPLAY_LIGHT in config: opts.pop(CONF_DISPLAY_LIGHT, False) opts[CONF_LIGHT] = config[CONF_DISPLAY_LIGHT] entry.options = {**opts} entry.version = 2 if entry.version == 2: # CONF_TYPE is not configurable, move it from options to the main config. config = {**entry.data, **entry.options, "name": entry.title} opts = {**entry.options} # Ensure type has been migrated. Some users are reporting errors which # suggest it was removed completely. But that is probably due to # overwriting options without CONF_TYPE. if config.get(CONF_TYPE, CONF_TYPE_AUTO) == CONF_TYPE_AUTO: device = setup_device(hass, config) config[CONF_TYPE] = await device.async_inferred_type() if config[CONF_TYPE] is None: return False entry.data = { CONF_DEVICE_ID: config[CONF_DEVICE_ID], CONF_LOCAL_KEY: config[CONF_LOCAL_KEY], CONF_HOST: config[CONF_HOST], CONF_TYPE: config[CONF_TYPE], } opts.pop(CONF_TYPE, None) entry.options = {**opts} entry.version = 3 if entry.version == 3: # Migrate to filename based config_type, to avoid needing to # parse config files to find the right one. config = {**entry.data, **entry.options, "name": entry.title} config_type = get_config(config[CONF_TYPE]).config_type # Special case for kogan_switch. Consider also v2. if config_type == "smartplugv1": device = setup_device(hass, config) config_type = await device.async_inferred_type() if config_type != "smartplugv2": config_type = "smartplugv1" entry.data = { CONF_DEVICE_ID: config[CONF_DEVICE_ID], CONF_LOCAL_KEY: config[CONF_LOCAL_KEY], CONF_HOST: config[CONF_HOST], CONF_TYPE: config_type, } entry.version = 4 return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): _LOGGER.debug(f"Setting up entry for device: {entry.data[CONF_DEVICE_ID]}") config = {**entry.data, **entry.options, "name": entry.title} setup_device(hass, config) device_conf = get_config(config[CONF_TYPE]) if device_conf is None: _LOGGER.error(f"Configuration file for {config[CONF_TYPE]} not found.") return False entities = {} e = device_conf.primary_entity if config.get(e.config_id, False): entities[e.entity] = True for e in device_conf.secondary_entities(): if config.get(e.config_id, False): entities[e.entity] = True for e in entities: hass.async_create_task(hass.config_entries.async_forward_entry_setup(entry, e)) entry.add_update_listener(async_update_entry) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): _LOGGER.debug(f"Unloading entry for device: {entry.data[CONF_DEVICE_ID]}") config = entry.data data = hass.data[DOMAIN][config[CONF_DEVICE_ID]] device_conf = get_config(config[CONF_TYPE]) if device_conf is None: _LOGGER.error(f"Configuration file for {config[CONF_TYPE]} not found.") return False entities = {} e = device_conf.primary_entity if e.config_id in data: entities[e.entity] = True for e in device_conf.secondary_entities(): if e.config_id in data: entities[e.entity] = True for e in entities: await hass.config_entries.async_forward_entry_unload(entry, e) delete_device(hass, config) del hass.data[DOMAIN][config[CONF_DEVICE_ID]] return True async def async_update_entry(hass: HomeAssistant, entry: ConfigEntry): _LOGGER.debug(f"Updating entry for device: {entry.data[CONF_DEVICE_ID]}") await async_unload_entry(hass, entry) await async_setup_entry(hass, entry)