diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index adee5cb..bdcd7ef 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -9,8 +9,9 @@ from homeassistant.const import ( CONF_ENTITIES, ) -from .const import DOMAIN +from .const import DOMAIN, TUYA_DEVICE from .config_flow import config_schema +from .common import TuyaDevice _LOGGER = logging.getLogger(__name__) @@ -19,23 +20,17 @@ UNSUB_LISTENER = "unsub_listener" CONFIG_SCHEMA = config_schema() -def import_from_yaml(hass, config, platform): - """Import configuration from YAML.""" - config[CONF_PLATFORM] = platform - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=config - ) - ) - - return True - - async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) - print("setup:", config.get(DOMAIN)) + for host_config in config.get(DOMAIN, []): + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data=host_config + ) + ) + return True @@ -45,12 +40,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): hass.data[DOMAIN][entry.entry_id] = { UNSUB_LISTENER: unsub_listener, + TUYA_DEVICE: TuyaDevice(entry.data), } - for platform in set(entity[CONF_PLATFORM] for entity in entry.data[CONF_ENTITIES]): + for entity in entry.data[CONF_ENTITIES]: + platform = entity[CONF_PLATFORM] hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) + return True diff --git a/custom_components/localtuya/binary_sensor.py b/custom_components/localtuya/binary_sensor.py index 4687058..92361ba 100644 --- a/custom_components/localtuya/binary_sensor.py +++ b/custom_components/localtuya/binary_sensor.py @@ -16,8 +16,6 @@ sensor: device_class: current """ import logging -from time import time, sleep -from threading import Lock import voluptuous as vol @@ -26,11 +24,7 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, BinarySensorEntity, ) -from homeassistant.const import ( - CONF_ID, - CONF_DEVICE_CLASS, - CONF_FRIENDLY_NAME, -) +from homeassistant.const import CONF_ID, CONF_DEVICE_CLASS from .common import LocalTuyaEntity, prepare_setup_entities @@ -51,7 +45,9 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya sensor based on a config entry.""" - device, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return @@ -59,7 +55,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device_config in entities_to_setup: sensors.append( LocaltuyaBinarySensor( - TuyaCache(device, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], ) @@ -68,74 +64,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(sensors, True) -class TuyaCache: - """Cache wrapper for pytuya.TuyaDevice.""" - - def __init__(self, device, friendly_name): - """Initialize the cache.""" - self._cached_status = "" - self._cached_status_time = 0 - self._device = device - self._friendly_name = friendly_name - self._lock = Lock() - - @property - def unique_id(self): - """Return unique device identifier.""" - return self._device.id - - def __get_status(self): - for i in range(5): - try: - status = self._device.status() - return status - except Exception: - print( - "Failed to update status of device [{}]".format( - self._device.address - ) - ) - sleep(1.0) - if i + 1 == 3: - _LOGGER.error( - "Failed to update status of device %s", self._device.address - ) - # return None - raise ConnectionError("Failed to update status .") - - def set_dps(self, state, dps_index): - """Change the Tuya sensor status and clear the cache.""" - self._cached_status = "" - self._cached_status_time = 0 - for i in range(5): - try: - return self._device.set_dps(state, dps_index) - except Exception: - print( - "Failed to set status of device [{}]".format(self._device.address) - ) - if i + 1 == 3: - _LOGGER.error( - "Failed to set status of device %s", self._device.address - ) - return - - # raise ConnectionError("Failed to set status.") - - def status(self): - """Get state of Tuya sensor and cache the results.""" - self._lock.acquire() - try: - now = time() - if not self._cached_status or now - self._cached_status_time > 15: - sleep(0.5) - self._cached_status = self.__get_status() - self._cached_status_time = time() - return self._cached_status - finally: - self._lock.release() - - class LocaltuyaBinarySensor(LocalTuyaEntity, BinarySensorEntity): """Representation of a Tuya binary sensor.""" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index dda3459..9e141eb 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -15,12 +15,12 @@ from homeassistant.const import ( ) from . import pytuya -from .const import CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, DOMAIN +from .const import CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, DOMAIN, TUYA_DEVICE _LOGGER = logging.getLogger(__name__) -def prepare_setup_entities(config_entry, platform): +def prepare_setup_entities(hass, config_entry, platform): """Prepare ro setup entities for a platform.""" entities_to_setup = [ entity @@ -30,16 +30,7 @@ def prepare_setup_entities(config_entry, platform): if not entities_to_setup: return None, None - tuyainterface = pytuya.TuyaInterface( - config_entry.data[CONF_DEVICE_ID], - config_entry.data[CONF_HOST], - config_entry.data[CONF_LOCAL_KEY], - float(config_entry.data[CONF_PROTOCOL_VERSION]), - ) - - for entity_config in entities_to_setup: - # this has to be done in case the device type is type_0d - tuyainterface.add_dps_to_request(entity_config[CONF_ID]) + tuyainterface = hass.data[DOMAIN][config_entry.entry_id][TUYA_DEVICE] return tuyainterface, entities_to_setup @@ -55,12 +46,20 @@ def get_entity_config(config_entry, dps_id): class TuyaDevice: """Cache wrapper for pytuya.TuyaInterface.""" - def __init__(self, interface, friendly_name): + def __init__(self, config_entry): """Initialize the cache.""" self._cached_status = "" self._cached_status_time = 0 - self._interface = interface - self._friendly_name = friendly_name + self._interface = pytuya.TuyaInterface( + config_entry[CONF_DEVICE_ID], + config_entry[CONF_HOST], + config_entry[CONF_LOCAL_KEY], + config_entry[CONF_PROTOCOL_VERSION], + ) + for entity in config_entry[CONF_ENTITIES]: + # this has to be done in case the device type is type_0d + self._interface.add_dps_to_request(entity[CONF_ID]) + self._friendly_name = config_entry[CONF_FRIENDLY_NAME] self._lock = Lock() @property @@ -90,7 +89,7 @@ class TuyaDevice: def set_dps(self, state, dps_index): # _LOGGER.info("running def set_dps from cover") - """Change the Tuya device status and clear the cache.""" + """Change the Tuya switch status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 for i in range(5): @@ -111,7 +110,7 @@ class TuyaDevice: # raise ConnectionError("Failed to set status.") def status(self): - """Get state of Tuya device and cache the results.""" + """Get state of Tuya switch and cache the results.""" _LOGGER.debug("running def status(self) from TuyaDevice") self._lock.acquire() try: diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index ef98ede..bd73ffd 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -13,7 +13,6 @@ from homeassistant.const import ( CONF_DEVICE_ID, CONF_FRIENDLY_NAME, CONF_PLATFORM, - CONF_SWITCHES, ) import homeassistant.helpers.config_validation as cv @@ -97,7 +96,6 @@ def schema_defaults(schema, dps_list=None, **defaults): if field.schema in defaults: field.default = vol.default_factory(defaults[field]) - return copy @@ -310,37 +308,21 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle import from YAML.""" def _convert_entity(conf): - converted = { - CONF_ID: conf[CONF_ID], - CONF_FRIENDLY_NAME: conf[CONF_FRIENDLY_NAME], - CONF_PLATFORM: self.platform, - } - for field in flow_schema(self.platform, self.dps_strings).keys(): - converted[str(field)] = conf[field] - return converted + for field in flow_schema(conf[CONF_PLATFORM], self.dps_strings).keys(): + if str(field) in conf: + conf[str(field)] = str(conf[field]) await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) - self.platform = user_input[CONF_PLATFORM] - if len(user_input.get(CONF_SWITCHES, [])) > 0: - for switch_conf in user_input[CONF_SWITCHES].values(): - self.entities.append(_convert_entity(switch_conf)) - else: - self.entities.append(_convert_entity(user_input)) + for entity_conf in user_input[CONF_ENTITIES]: + entity_conf[CONF_ID] = str(entity_conf[CONF_ID]) + _convert_entity(entity_conf) - # print('ENTITIES: [{}] '.format(self.entities)) - config = { - CONF_FRIENDLY_NAME: f"{user_input[CONF_FRIENDLY_NAME]}", - CONF_HOST: user_input[CONF_HOST], - CONF_DEVICE_ID: user_input[CONF_DEVICE_ID], - CONF_LOCAL_KEY: user_input[CONF_LOCAL_KEY], - CONF_PROTOCOL_VERSION: user_input[CONF_PROTOCOL_VERSION], - CONF_YAML_IMPORT: True, - CONF_ENTITIES: self.entities, - } - self._abort_if_unique_id_configured(updates=config) + user_input[CONF_YAML_IMPORT] = True + + self._abort_if_unique_id_configured(updates=user_input) return self.async_create_entry( - title=f"{config[CONF_FRIENDLY_NAME]} (YAML)", data=config + title=f"{user_input[CONF_FRIENDLY_NAME]} (YAML)", data=user_input ) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index d49a16d..42b9bc9 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -26,3 +26,5 @@ DOMAIN = "localtuya" # Platforms in this list must support config flows PLATFORMS = ["binary_sensor", "cover", "fan", "light", "sensor", "switch"] + +TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index ad8a1ce..1fb4a36 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -31,17 +31,14 @@ from homeassistant.components.cover import ( SUPPORT_STOP, SUPPORT_SET_POSITION, ) -from homeassistant.const import ( - CONF_ID, - CONF_FRIENDLY_NAME, -) +from homeassistant.const import CONF_ID from .const import ( CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD, ) -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) @@ -61,7 +58,9 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya cover based on a config entry.""" - tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return @@ -69,7 +68,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device_config in entities_to_setup: covers.append( LocaltuyaCover( - TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], ) @@ -185,7 +184,7 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity): def stop_cover(self, **kwargs): """Stop the cover.""" - _LOGGER.debug("Laudebugching command %s to cover ", self._config[CONF_STOP_CMD]) + _LOGGER.debug("Launching command %s to cover ", self._config[CONF_STOP_CMD]) self._device.set_dps(self._config[CONF_STOP_CMD], self._dps_id) def status_updated(self): diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 771032e..4ab07cb 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -26,9 +26,9 @@ from homeassistant.components.fan import ( SUPPORT_SET_SPEED, SUPPORT_OSCILLATE, ) -from homeassistant.const import CONF_ID, CONF_FRIENDLY_NAME +from homeassistant.const import CONF_ID -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) @@ -40,7 +40,9 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya fan based on a config entry.""" - tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return @@ -49,7 +51,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device_config in entities_to_setup: fans.append( LocaltuyaFan( - TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], ) diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index b05639a..b8ddb9e 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -13,10 +13,7 @@ light: """ import logging -from homeassistant.const import ( - CONF_ID, - CONF_FRIENDLY_NAME, -) +from homeassistant.const import CONF_ID from homeassistant.components.light import ( LightEntity, DOMAIN, @@ -27,7 +24,7 @@ from homeassistant.components.light import ( SUPPORT_COLOR, ) -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) @@ -49,18 +46,17 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya light based on a config entry.""" - tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return lights = [] for device_config in entities_to_setup: - # this has to be done in case the device type is type_0d - tuyainterface.add_dps_to_request(device_config[CONF_ID]) - lights.append( LocaltuyaLight( - TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], ) diff --git a/custom_components/localtuya/sensor.py b/custom_components/localtuya/sensor.py index 77a36b4..60f1fe5 100644 --- a/custom_components/localtuya/sensor.py +++ b/custom_components/localtuya/sensor.py @@ -22,13 +22,12 @@ from homeassistant.components.sensor import DOMAIN, DEVICE_CLASSES from homeassistant.const import ( CONF_ID, CONF_DEVICE_CLASS, - CONF_FRIENDLY_NAME, CONF_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, ) from .const import CONF_SCALING -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) @@ -48,7 +47,9 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya sensor based on a config entry.""" - tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return @@ -56,7 +57,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device_config in entities_to_setup: sensors.append( LocaltuyaSensor( - TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], ) diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index ef2ec7f..381809d 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -32,10 +32,7 @@ from homeassistant.components.switch import ( SwitchEntity, DOMAIN, ) -from homeassistant.const import ( - CONF_ID, - CONF_FRIENDLY_NAME, -) +from homeassistant.const import CONF_ID from .const import ( ATTR_CURRENT, @@ -45,12 +42,10 @@ from .const import ( CONF_CURRENT_CONSUMPTION, CONF_VOLTAGE, ) -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) -DEFAULT_ID = "1" - def flow_schema(dps): """Return schema used in config flow.""" @@ -63,24 +58,17 @@ def flow_schema(dps): async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a Tuya switch based on a config entry.""" - tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN) + tuyainterface, entities_to_setup = prepare_setup_entities( + hass, config_entry, DOMAIN + ) if not entities_to_setup: return switches = [] for device_config in entities_to_setup: - if device_config.get(CONF_CURRENT, "-1") != "-1": - tuyainterface.add_dps_to_request(device_config.get(CONF_CURRENT)) - if device_config.get(CONF_CURRENT_CONSUMPTION, "-1") != "-1": - tuyainterface.add_dps_to_request( - device_config.get(CONF_CURRENT_CONSUMPTION) - ) - if device_config.get(CONF_VOLTAGE, "-1") != "-1": - tuyainterface.add_dps_to_request(device_config.get(CONF_VOLTAGE)) - switches.append( LocaltuyaSwitch( - TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]), + tuyainterface, config_entry, device_config[CONF_ID], )