diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 36dd9cc4f..1650edcec 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -1,65 +1,83 @@ -"""The LocalTuya integration integration.""" +"""The LocalTuya integration integration. + +Sample YAML config with all supported entity types (default values +are pre-filled for optional fields): + +localtuya: + - host: 192.168.1.x + device_id: xxxxx + local_key: xxxxx + friendly_name: Tuya Device + protocol_version: "3.3" + entities: + - platform: binary_sensor + friendly_name: Plug Status + id: 1 + device_class: power + state_on: "true" # Optional + state_off: "false" # Optional + + - platform: cover + friendly_name: Device Cover + id: 2 + open_cmd: "on" # Optional + close_cmd: "off" # Optional + stop_cmd: "stop" # Optional + + - platform: fan + friendly_name: Device Fan + id: 3 + + - platform: light + friendly_name: Device Light + id: 4 + + - platform: sensor + friendly_name: Plug Voltage + id: 20 + scaling: 0.1 # Optional + device_class: voltage # Optional + unit_of_measurement: "V" # Optional + + - platform: switch + friendly_name: Plug + id: 1 + current: 18 # Optional + current_consumption: 19 # Optional + voltage: 20 # Optional +""" import asyncio import logging -import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.const import ( - CONF_DEVICE_ID, - CONF_ID, - CONF_ICON, - CONF_NAME, - CONF_FRIENDLY_NAME, - CONF_HOST, CONF_PLATFORM, CONF_ENTITIES, ) -import homeassistant.helpers.config_validation as cv -from .const import CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, DOMAIN - - -import pprint - -pp = pprint.PrettyPrinter(indent=4) +from .const import DOMAIN, TUYA_DEVICE +from .config_flow import config_schema +from .common import TuyaDevice _LOGGER = logging.getLogger(__name__) -DEFAULT_ID = "1" -DEFAULT_PROTOCOL_VERSION = 3.3 - UNSUB_LISTENER = "unsub_listener" -BASE_PLATFORM_SCHEMA = { - vol.Optional(CONF_ICON): cv.icon, # Deprecated: not used - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_DEVICE_ID): cv.string, - vol.Required(CONF_LOCAL_KEY): cv.string, - vol.Optional(CONF_NAME): cv.string, # Deprecated: not used - vol.Required(CONF_FRIENDLY_NAME): cv.string, - vol.Required(CONF_PROTOCOL_VERSION, default=DEFAULT_PROTOCOL_VERSION): vol.Coerce( - float - ), - vol.Optional(CONF_ID, default=DEFAULT_ID): cv.string, -} - - -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 +CONFIG_SCHEMA = config_schema() async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(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 @@ -69,12 +87,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 2891441d8..2d7ecb2f3 100644 --- a/custom_components/localtuya/binary_sensor.py +++ b/custom_components/localtuya/binary_sensor.py @@ -1,52 +1,22 @@ -""" -Platform to prsent any Tuya DP as a binary sensor. - -Sample config yaml - -sensor: - - platform: localtuya - host: 192.168.0.1 - local_key: 1234567891234567 - device_id: 12345678912345671234 - friendly_name: Current - protocol_version: 3.3 - id: 18 - state_on: "true" (optional, default is "true") - state_off: "false" (optional, default is "false") - device_class: current -""" +"""Platform to present any Tuya DP as a binary sensor.""" import logging -from time import time, sleep -from threading import Lock import voluptuous as vol from homeassistant.components.binary_sensor import ( DOMAIN, - PLATFORM_SCHEMA, 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 . import ( - BASE_PLATFORM_SCHEMA, - LocalTuyaEntity, - prepare_setup_entities, - import_from_yaml, -) +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) CONF_STATE_ON = "state_on" CONF_STATE_OFF = "state_off" -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA) - def flow_schema(dps): """Return schema used in config flow.""" @@ -59,7 +29,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 @@ -67,7 +39,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], ) @@ -76,79 +48,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(sensors, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya sensor.""" - return import_from_yaml(hass, config, DOMAIN) - - -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 dda34599b..91bb2267b 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -15,12 +15,12 @@ ) 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], + float(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 @@ def __get_status(self): 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 @@ def set_dps(self, state, dps_index): # 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 070f1c7d4..431ef8777 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -13,8 +13,8 @@ CONF_DEVICE_ID, CONF_FRIENDLY_NAME, CONF_PLATFORM, - CONF_SWITCHES, ) +import homeassistant.helpers.config_validation as cv from . import pytuya from .const import ( # pylint: disable=unused-import @@ -56,6 +56,16 @@ } ) +DEVICE_SCHEMA = vol.Schema( + { + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_DEVICE_ID): cv.string, + vol.Required(CONF_LOCAL_KEY): cv.string, + vol.Required(CONF_FRIENDLY_NAME): cv.string, + vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), + } +) + PICK_ENTITY_SCHEMA = vol.Schema( {vol.Required(PLATFORM_TO_ADD, default=PLATFORMS[0]): vol.In(PLATFORMS)} ) @@ -86,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 @@ -100,9 +109,12 @@ def gen_dps_strings(): return [f"{dp} (value: ?)" for dp in range(1, 256)] -def platform_schema(platform, dps_strings, allow_id=True): +def platform_schema(platform, dps_strings, allow_id=True, yaml=False): """Generate input validation schema for a platform.""" schema = {} + if yaml: + # In YAML mode we force the specified platform to match flow schema + schema[vol.Required(CONF_PLATFORM)] = vol.In([platform]) if allow_id: schema[vol.Required(CONF_ID)] = vol.In(dps_strings) schema[vol.Required(CONF_FRIENDLY_NAME)] = str @@ -120,12 +132,32 @@ def strip_dps_values(user_input, dps_strings): stripped = {} for field, value in user_input.items(): if value in dps_strings: - stripped[field] = user_input[field].split(" ")[0] + stripped[field] = int(user_input[field].split(" ")[0]) else: stripped[field] = user_input[field] return stripped +def config_schema(): + """Build schema used for setting up component.""" + entity_schemas = [ + platform_schema(platform, range(1, 256), yaml=True) for platform in PLATFORMS + ] + return vol.Schema( + { + DOMAIN: vol.All( + cv.ensure_list, + [ + DEVICE_SCHEMA.extend( + {vol.Required(CONF_ENTITIES): [vol.Any(*entity_schemas)]} + ) + ], + ) + }, + extra=vol.ALLOW_EXTRA, + ) + + async def validate_input(hass: core.HomeAssistant, data): """Validate the user input allows us to connect.""" tuyainterface = pytuya.TuyaInterface( @@ -274,39 +306,13 @@ async def async_step_add_entity(self, user_input=None): async def async_step_import(self, user_input): """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 - 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)) - - # 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 d49a16d91..42b9bc9c3 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -26,3 +26,5 @@ # 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 410341296..ec2d1e9ca 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -1,23 +1,4 @@ -""" -Simple platform to locally control Tuya-based cover devices. - -Sample config yaml: - -cover: - - platform: localtuya #REQUIRED - host: 192.168.0.123 #REQUIRED - local_key: 1234567891234567 #REQUIRED - device_id: 123456789123456789abcd #REQUIRED - name: cover_guests #REQUIRED - friendly_name: Cover guests #REQUIRED - protocol_version: 3.3 #REQUIRED - id: 1 #OPTIONAL - icon: mdi:blinds #OPTIONAL - open_cmd: open #OPTIONAL, default is 'on' - close_cmd: close #OPTIONAL, default is 'off' - stop_cmd: stop #OPTIONAL, default is 'stop' - -""" +"""Platform to locally control Tuya-based cover devices.""" import logging from time import sleep @@ -26,25 +7,19 @@ from homeassistant.components.cover import ( CoverEntity, DOMAIN, - PLATFORM_SCHEMA, SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_STOP, SUPPORT_SET_POSITION, ) -from homeassistant.const import ( - CONF_ID, - CONF_FRIENDLY_NAME, -) -import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_ID -from . import BASE_PLATFORM_SCHEMA, import_from_yaml 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__) @@ -53,15 +28,6 @@ DEFAULT_STOP_CMD = "stop" -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA).extend( - { - vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): cv.string, - vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): cv.string, - vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): cv.string, - } -) - - def flow_schema(dps): """Return schema used in config flow.""" return { @@ -73,7 +39,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 @@ -81,7 +49,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], ) @@ -90,11 +58,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(covers, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya cover.""" - return import_from_yaml(hass, config, DOMAIN) - - class LocaltuyaCover(LocalTuyaEntity, CoverEntity): """Tuya cover device.""" @@ -202,7 +165,7 @@ def close_cover(self, **kwargs): 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 50dcf49c3..10776720b 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,25 +1,9 @@ -""" -Simple platform to control LOCALLY Tuya cover devices. - -Sample config yaml - -fan: - - platform: localtuya - host: 192.168.0.123 - local_key: 1234567891234567 - device_id: 123456789123456789abcd - name: fan guests - friendly_name: fan guests - protocol_version: 3.3 - id: 1 - -""" +"""Platform to locally control Tuya-based fan devices.""" import logging from homeassistant.components.fan import ( FanEntity, DOMAIN, - PLATFORM_SCHEMA, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, @@ -27,18 +11,12 @@ SUPPORT_SET_SPEED, SUPPORT_OSCILLATE, ) -from homeassistant.const import CONF_ID, CONF_FRIENDLY_NAME +from homeassistant.const import CONF_ID -from . import ( - BASE_PLATFORM_SCHEMA, - import_from_yaml, -) -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA) - def flow_schema(dps): """Return schema used in config flow.""" @@ -47,7 +25,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 @@ -56,7 +36,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], ) @@ -65,11 +45,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(fans, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya fan.""" - return import_from_yaml(hass, config, DOMAIN) - - class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Representation of a Tuya fan.""" diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index a423ffe21..0f3ca7c79 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -1,26 +1,10 @@ -""" -Simple platform to control LOCALLY Tuya light devices. - -Sample config yaml - -light: - - platform: localtuya - host: 192.168.0.1 - local_key: 1234567891234567 - device_id: 12345678912345671234 - friendly_name: This Light - protocol_version: 3.3 -""" +"""Platform to locally control Tuya-based light devices.""" import logging -from homeassistant.const import ( - CONF_ID, - CONF_FRIENDLY_NAME, -) +from homeassistant.const import CONF_ID from homeassistant.components.light import ( LightEntity, DOMAIN, - PLATFORM_SCHEMA, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, @@ -28,12 +12,7 @@ SUPPORT_COLOR, ) -from . import ( - BASE_PLATFORM_SCHEMA, - import_from_yaml, -) -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities - +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) @@ -47,8 +26,6 @@ DPS_INDEX_COLOURTEMP = "4" DPS_INDEX_COLOUR = "5" -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA) - def flow_schema(dps): """Return schema used in config flow.""" @@ -57,18 +34,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], ) @@ -77,11 +53,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(lights, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya light.""" - return import_from_yaml(hass, config, DOMAIN) - - class LocaltuyaLight(LocalTuyaEntity, LightEntity): """Representation of a Tuya light.""" diff --git a/custom_components/localtuya/sensor.py b/custom_components/localtuya/sensor.py index 294c5fad2..870e48aa7 100644 --- a/custom_components/localtuya/sensor.py +++ b/custom_components/localtuya/sensor.py @@ -1,44 +1,23 @@ -""" -Platform to prsent any Tuya DP as a sensor. - -Sample config yaml - -sensor: - - platform: localtuya - host: 192.168.0.1 - local_key: 1234567891234567 - device_id: 12345678912345671234 - friendly_name: Current - protocol_version: 3.3 - id: 18 - unit_of_measurement: mA - device_class: current -""" +"""Platform to present any Tuya DP as a sensor.""" import logging import voluptuous as vol -from homeassistant.components.sensor import DOMAIN, PLATFORM_SCHEMA, DEVICE_CLASSES +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 . import ( - BASE_PLATFORM_SCHEMA, - import_from_yaml, -) from .const import CONF_SCALING -from .common import LocalTuyaEntity, TuyaDevice, prepare_setup_entities +from .common import LocalTuyaEntity, prepare_setup_entities _LOGGER = logging.getLogger(__name__) DEFAULT_SCALING = 1.0 - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA) +DEFAULT_PRECISION = 2 def flow_schema(dps): @@ -54,7 +33,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 @@ -62,7 +43,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], ) @@ -71,11 +52,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(sensors, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya sensor.""" - return import_from_yaml(hass, config, DOMAIN) - - class LocaltuyaSensor(LocalTuyaEntity): """Representation of a Tuya sensor.""" @@ -93,9 +69,6 @@ def __init__( @property def state(self): """Return sensor state.""" - scale_factor = self._config.get(CONF_SCALING) - if scale_factor is not None: - return self._state * scale_factor return self._state @property @@ -110,4 +83,8 @@ def unit_of_measurement(self): def status_updated(self): """Device status was updated.""" - self._state = self.dps(self._dps_id) + state = self.dps(self._dps_id) + scale_factor = self._config.get(CONF_SCALING) + if scale_factor is not None: + state = round(state * scale_factor, DEFAULT_PRECISION) + self._state = state diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index 83d061d4a..0ae6075fe 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -1,29 +1,4 @@ -""" -Simple platform to control LOCALLY Tuya switch devices. - -Sample config yaml - -switch: - - platform: localtuya - host: 192.168.0.1 - local_key: 1234567891234567 - device_id: 12345678912345671234 - name: tuya_01 - friendly_name: tuya_01 - protocol_version: 3.3 - switches: - sw01: - name: main_plug - friendly_name: Main Plug - id: 1 - current: 18 - current_consumption: 19 - voltage: 20 - sw02: - name: usb_plug - friendly_name: USB Plug - id: 7 -""" +"""Platform to locally control Tuya-based switch devices.""" import logging import voluptuous as vol @@ -31,20 +6,9 @@ from homeassistant.components.switch import ( SwitchEntity, DOMAIN, - PLATFORM_SCHEMA, ) -from homeassistant.const import ( - CONF_ID, - CONF_SWITCHES, - CONF_FRIENDLY_NAME, - CONF_NAME, -) -import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_ID -from . import ( - BASE_PLATFORM_SCHEMA, - import_from_yaml, -) from .const import ( ATTR_CURRENT, ATTR_CURRENT_CONSUMPTION, @@ -53,33 +17,10 @@ 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" - -# TODO: This will eventully merge with flow_schema -SWITCH_SCHEMA = vol.Schema( - { - vol.Optional(CONF_ID, default=DEFAULT_ID): cv.string, - vol.Optional(CONF_NAME): cv.string, # Deprecated: not used - vol.Required(CONF_FRIENDLY_NAME): cv.string, - vol.Optional(CONF_CURRENT, default="-1"): cv.string, - vol.Optional(CONF_CURRENT_CONSUMPTION, default="-1"): cv.string, - vol.Optional(CONF_VOLTAGE, default="-1"): cv.string, - } -) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA).extend( - { - vol.Optional(CONF_CURRENT, default="-1"): cv.string, - vol.Optional(CONF_CURRENT_CONSUMPTION, default="-1"): cv.string, - vol.Optional(CONF_VOLTAGE, default="-1"): cv.string, - vol.Optional(CONF_SWITCHES, default={}): vol.Schema({cv.slug: SWITCH_SCHEMA}), - } -) - def flow_schema(dps): """Return schema used in config flow.""" @@ -92,24 +33,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], ) @@ -118,11 +52,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(switches, True) -def setup_platform(hass, config, add_devices, discovery_info=None): - """Set up of the Tuya switch.""" - return import_from_yaml(hass, config, DOMAIN) - - class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity): """Representation of a Tuya switch."""