Introduced migration; fixed update when adding device
This commit is contained in:
@@ -11,12 +11,16 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
CONF_CLIENT_ID,
|
||||||
|
CONF_CLIENT_SECRET,
|
||||||
|
CONF_REGION,
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_DEVICES,
|
CONF_DEVICES,
|
||||||
CONF_ENTITIES,
|
CONF_ENTITIES,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
|
CONF_USERNAME,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
SERVICE_RELOAD,
|
SERVICE_RELOAD,
|
||||||
)
|
)
|
||||||
@@ -31,10 +35,11 @@ from .config_flow import config_schema
|
|||||||
from .const import (
|
from .const import (
|
||||||
ATTR_UPDATED_AT,
|
ATTR_UPDATED_AT,
|
||||||
CONF_PRODUCT_KEY,
|
CONF_PRODUCT_KEY,
|
||||||
|
CONF_USER_ID,
|
||||||
DATA_CLOUD,
|
DATA_CLOUD,
|
||||||
DATA_DISCOVERY,
|
DATA_DISCOVERY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
TUYA_DEVICE
|
TUYA_DEVICES,
|
||||||
)
|
)
|
||||||
from .discovery import TuyaDiscovery
|
from .discovery import TuyaDiscovery
|
||||||
|
|
||||||
@@ -62,6 +67,7 @@ SERVICE_SET_DP_SCHEMA = vol.Schema(
|
|||||||
async def async_setup(hass: HomeAssistant, config: dict):
|
async def async_setup(hass: HomeAssistant, config: dict):
|
||||||
"""Set up the LocalTuya integration component."""
|
"""Set up the LocalTuya integration component."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
print("SETUP")
|
||||||
|
|
||||||
device_cache = {}
|
device_cache = {}
|
||||||
|
|
||||||
@@ -84,14 +90,11 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
|
|
||||||
async def _handle_set_dp(event):
|
async def _handle_set_dp(event):
|
||||||
"""Handle set_dp service call."""
|
"""Handle set_dp service call."""
|
||||||
entry = async_config_entry_by_device_id(hass, event.data[CONF_DEVICE_ID])
|
dev_id = event.data[CONF_DEVICE_ID]
|
||||||
if not entry:
|
if dev_id not in hass.data[DOMAIN][TUYA_DEVICES]:
|
||||||
raise HomeAssistantError("unknown device id")
|
raise HomeAssistantError("unknown device id")
|
||||||
|
|
||||||
if entry.entry_id not in hass.data[DOMAIN]:
|
device = hass.data[DOMAIN][TUYA_DEVICES][dev_id]
|
||||||
raise HomeAssistantError("device has not been discovered")
|
|
||||||
|
|
||||||
device = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE]
|
|
||||||
if not device.connected:
|
if not device.connected:
|
||||||
raise HomeAssistantError("not connected to device")
|
raise HomeAssistantError("not connected to device")
|
||||||
|
|
||||||
@@ -138,21 +141,9 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
elif entry.entry_id in hass.data[DOMAIN]:
|
elif entry.entry_id in hass.data[DOMAIN]:
|
||||||
_LOGGER.debug("Device %s found with IP %s", device_id, device_ip)
|
_LOGGER.debug("Device %s found with IP %s", device_id, device_ip)
|
||||||
|
|
||||||
device = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE]
|
device = hass.data[DOMAIN][TUYA_DEVICES][device_id]
|
||||||
device.async_connect()
|
device.async_connect()
|
||||||
|
|
||||||
client_id = 'xx'
|
|
||||||
secret = 'xx'
|
|
||||||
uid = 'xx'
|
|
||||||
tuya_api = TuyaCloudApi(hass, "eu", client_id, secret, uid)
|
|
||||||
res = await tuya_api.async_get_access_token()
|
|
||||||
_LOGGER.debug("ACCESS TOKEN RES: %s", res)
|
|
||||||
res = await tuya_api.async_get_devices_list()
|
|
||||||
for dev_id, dev in tuya_api._device_list.items():
|
|
||||||
print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ")
|
|
||||||
hass.data[DOMAIN][DATA_CLOUD] = tuya_api
|
|
||||||
|
|
||||||
_LOGGER.debug("\n\nSTARTING DISCOVERY")
|
|
||||||
discovery = TuyaDiscovery(_device_discovered)
|
discovery = TuyaDiscovery(_device_discovered)
|
||||||
|
|
||||||
def _shutdown(event):
|
def _shutdown(event):
|
||||||
@@ -168,13 +159,16 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
|
|
||||||
async def _async_reconnect(now):
|
async def _async_reconnect(now):
|
||||||
"""Try connecting to devices not already connected to."""
|
"""Try connecting to devices not already connected to."""
|
||||||
for entry_id, value in hass.data[DOMAIN].items():
|
for device in hass.data[DOMAIN][TUYA_DEVICES]:
|
||||||
if entry_id == DATA_DISCOVERY:
|
|
||||||
continue
|
|
||||||
|
|
||||||
device = value[TUYA_DEVICE]
|
|
||||||
if not device.connected:
|
if not device.connected:
|
||||||
device.async_connect()
|
device.async_connect()
|
||||||
|
# for entry_id, value in hass.data[DOMAIN].items():
|
||||||
|
# if entry_id == DATA_DISCOVERY:
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# device = value[TUYA_DEVICE]
|
||||||
|
# if not device.connected:
|
||||||
|
# device.async_connect()
|
||||||
|
|
||||||
async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL)
|
async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL)
|
||||||
|
|
||||||
@@ -191,16 +185,69 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_migrate_entry(domain):
|
async def async_migrate_entry(hass, config_entry: ConfigEntry):
|
||||||
"""Migrate old entry."""
|
"""Migrate old entries merging all of them in one."""
|
||||||
print("WHYYYYYYY")
|
new_version = 2
|
||||||
_LOGGER.error("WHHHYYYYYY")
|
if config_entry.version == 1:
|
||||||
|
_LOGGER.debug("Migrating config entry from version %s", config_entry.version)
|
||||||
|
stored_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
|
||||||
|
if config_entry.entry_id == stored_entries[0].entry_id:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Migrating the first config entry (%s)", config_entry.entry_id
|
||||||
|
)
|
||||||
|
new_data = {}
|
||||||
|
new_data[CONF_REGION] = "eu"
|
||||||
|
new_data[CONF_CLIENT_ID] = "xxx"
|
||||||
|
new_data[CONF_CLIENT_SECRET] = "xxx"
|
||||||
|
new_data[CONF_USER_ID] = "xxx"
|
||||||
|
new_data[CONF_USERNAME] = DOMAIN
|
||||||
|
new_data[CONF_DEVICES] = {
|
||||||
|
config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()
|
||||||
|
}
|
||||||
|
config_entry.version = new_version
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
config_entry, title=DOMAIN, data=new_data
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Merging the config entry %s into the main one", config_entry.entry_id
|
||||||
|
)
|
||||||
|
new_data = stored_entries[0].data.copy()
|
||||||
|
new_data[CONF_DEVICES].update(
|
||||||
|
{config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()}
|
||||||
|
)
|
||||||
|
hass.config_entries.async_update_entry(stored_entries[0], data=new_data)
|
||||||
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
|
|
||||||
|
_LOGGER.info(
|
||||||
|
"Entry %s successfully migrated to version %s.",
|
||||||
|
config_entry.entry_id,
|
||||||
|
new_version,
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
print("SETUP ENTRY STARTED!")
|
||||||
"""Set up LocalTuya integration from a config entry."""
|
"""Set up LocalTuya integration from a config entry."""
|
||||||
unsub_listener = entry.add_update_listener(update_listener)
|
unsub_listener = entry.add_update_listener(update_listener)
|
||||||
|
hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener
|
||||||
|
hass.data[DOMAIN][TUYA_DEVICES] = {}
|
||||||
|
|
||||||
|
region = entry.data[CONF_REGION]
|
||||||
|
client_id = entry.data[CONF_CLIENT_ID]
|
||||||
|
secret = entry.data[CONF_CLIENT_SECRET]
|
||||||
|
user_id = entry.data[CONF_USER_ID]
|
||||||
|
tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id)
|
||||||
|
res = await tuya_api.async_get_access_token()
|
||||||
|
_LOGGER.debug("ACCESS TOKEN RES: %s . CLOUD DEVICES:", res)
|
||||||
|
res = await tuya_api.async_get_devices_list()
|
||||||
|
for dev_id, dev in tuya_api._device_list.items():
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Name: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"]
|
||||||
|
)
|
||||||
|
hass.data[DOMAIN][DATA_CLOUD] = tuya_api
|
||||||
|
|
||||||
# device = TuyaDevice(hass, entry.data)
|
# device = TuyaDevice(hass, entry.data)
|
||||||
#
|
#
|
||||||
@@ -212,13 +259,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
async def setup_entities(dev_id):
|
async def setup_entities(dev_id):
|
||||||
dev_entry = entry.data[CONF_DEVICES][dev_id]
|
dev_entry = entry.data[CONF_DEVICES][dev_id]
|
||||||
device = TuyaDevice(hass, dev_entry)
|
device = TuyaDevice(hass, dev_entry)
|
||||||
hass.data[DOMAIN][dev_id] = {
|
hass.data[DOMAIN][TUYA_DEVICES][dev_id] = device
|
||||||
UNSUB_LISTENER: unsub_listener,
|
|
||||||
TUYA_DEVICE: device,
|
|
||||||
}
|
|
||||||
|
|
||||||
platforms = set(entity[CONF_PLATFORM] for entity in dev_entry[CONF_ENTITIES])
|
platforms = set(entity[CONF_PLATFORM] for entity in dev_entry[CONF_ENTITIES])
|
||||||
print("DEV {} platforms: {}".format(dev_id, platforms))
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*[
|
*[
|
||||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||||
@@ -228,56 +271,64 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
device.async_connect()
|
device.async_connect()
|
||||||
|
|
||||||
await async_remove_orphan_entities(hass, entry)
|
await async_remove_orphan_entities(hass, entry)
|
||||||
|
print("SETUP_ENTITIES for {} ENDED".format(dev_id))
|
||||||
|
|
||||||
for dev_id in entry.data[CONF_DEVICES]:
|
for dev_id in entry.data[CONF_DEVICES]:
|
||||||
hass.async_create_task(setup_entities(dev_id))
|
hass.async_create_task(setup_entities(dev_id))
|
||||||
|
|
||||||
|
print("SETUP ENTRY ENDED!")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
|
# print("ASYNC_UNLOAD_ENTRY INVOKED...")
|
||||||
|
platforms = {}
|
||||||
|
for dev_id, dev_entry in entry.data[CONF_DEVICES].items():
|
||||||
|
for entity in dev_entry[CONF_ENTITIES]:
|
||||||
|
platforms[entity[CONF_PLATFORM]] = True
|
||||||
|
|
||||||
unload_ok = all(
|
unload_ok = all(
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*[
|
*[
|
||||||
hass.config_entries.async_forward_entry_unload(entry, component)
|
hass.config_entries.async_forward_entry_unload(entry, component)
|
||||||
for component in set(
|
for component in platforms
|
||||||
entity[CONF_PLATFORM] for entity in entry.data[CONF_ENTITIES]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]()
|
hass.data[DOMAIN][UNSUB_LISTENER]()
|
||||||
await hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE].close()
|
for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items():
|
||||||
if unload_ok:
|
await device.close()
|
||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
|
||||||
|
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN][TUYA_DEVICES] = {}
|
||||||
|
|
||||||
|
# print("ASYNC_UNLOAD_ENTRY ENDED")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def update_listener(hass, config_entry):
|
async def update_listener(hass, config_entry):
|
||||||
"""Update listener."""
|
"""Update listener."""
|
||||||
|
print("UPDATE_LISTENER INVOKED")
|
||||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||||
|
print("UPDATE_LISTENER RELOADED {}".format(config_entry.entry_id))
|
||||||
|
|
||||||
|
|
||||||
async def async_remove_config_entry_device(
|
async def async_remove_config_entry_device(
|
||||||
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry
|
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Remove a config entry from a device."""
|
"""Remove a config entry from a device."""
|
||||||
print("REMOVING {} FROM {}".format(device_entry.identifiers, config_entry.data))
|
|
||||||
dev_id = list(device_entry.identifiers)[0][1].split("_")[-1]
|
dev_id = list(device_entry.identifiers)[0][1].split("_")[-1]
|
||||||
_LOGGER.debug("Removing %s", dev_id)
|
|
||||||
|
|
||||||
if dev_id not in config_entry.data[CONF_DEVICES]:
|
if dev_id not in config_entry.data[CONF_DEVICES]:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Device ID %s not found in config entry: finalizing device removal",
|
"Device ID %s not found in config entry: finalizing device removal", dev_id
|
||||||
dev_id
|
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_LOGGER.debug("Closing device connection for %s", dev_id)
|
await hass.data[DOMAIN][TUYA_DEVICES][dev_id].close()
|
||||||
await hass.data[DOMAIN][dev_id][TUYA_DEVICE].close()
|
|
||||||
|
|
||||||
new_data = config_entry.data.copy()
|
new_data = config_entry.data.copy()
|
||||||
new_data[CONF_DEVICES].pop(dev_id)
|
new_data[CONF_DEVICES].pop(dev_id)
|
||||||
@@ -287,7 +338,6 @@ async def async_remove_config_entry_device(
|
|||||||
config_entry,
|
config_entry,
|
||||||
data=new_data,
|
data=new_data,
|
||||||
)
|
)
|
||||||
_LOGGER.debug("Config entry updated")
|
|
||||||
|
|
||||||
ent_reg = await er.async_get_registry(hass)
|
ent_reg = await er.async_get_registry(hass)
|
||||||
entities = {
|
entities = {
|
||||||
@@ -298,13 +348,13 @@ async def async_remove_config_entry_device(
|
|||||||
for entity_id in entities.values():
|
for entity_id in entities.values():
|
||||||
ent_reg.async_remove(entity_id)
|
ent_reg.async_remove(entity_id)
|
||||||
print("REMOVED {}".format(entity_id))
|
print("REMOVED {}".format(entity_id))
|
||||||
_LOGGER.debug("Removed %s entities: finalizing device removal", len(entities))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_remove_orphan_entities(hass, entry):
|
async def async_remove_orphan_entities(hass, entry):
|
||||||
"""Remove entities associated with config entry that has been removed."""
|
"""Remove entities associated with config entry that has been removed."""
|
||||||
|
return
|
||||||
ent_reg = await er.async_get_registry(hass)
|
ent_reg = await er.async_get_registry(hass)
|
||||||
entities = {
|
entities = {
|
||||||
ent.unique_id: ent.entity_id
|
ent.unique_id: ent.entity_id
|
||||||
@@ -312,7 +362,7 @@ async def async_remove_orphan_entities(hass, entry):
|
|||||||
}
|
}
|
||||||
print("ENTITIES ORPHAN {}".format(entities))
|
print("ENTITIES ORPHAN {}".format(entities))
|
||||||
return
|
return
|
||||||
res = ent_reg.async_remove('switch.aa')
|
res = ent_reg.async_remove("switch.aa")
|
||||||
print("RESULT ORPHAN {}".format(res))
|
print("RESULT ORPHAN {}".format(res))
|
||||||
# del entities[101]
|
# del entities[101]
|
||||||
|
|
||||||
|
@@ -127,7 +127,7 @@ class TuyaCloudApi:
|
|||||||
)
|
)
|
||||||
return f"Error {r_json['code']}: {r_json['msg']}"
|
return f"Error {r_json['code']}: {r_json['msg']}"
|
||||||
|
|
||||||
self._device_list = {dev['id']: dev for dev in r_json["result"]}
|
self._device_list = {dev["id"]: dev for dev in r_json["result"]}
|
||||||
# print("DEV__LIST: {}".format(self._device_list))
|
# print("DEV__LIST: {}".format(self._device_list))
|
||||||
|
|
||||||
return "ok"
|
return "ok"
|
||||||
|
@@ -28,7 +28,7 @@ from .const import (
|
|||||||
CONF_PROTOCOL_VERSION,
|
CONF_PROTOCOL_VERSION,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
DATA_CLOUD,
|
DATA_CLOUD,
|
||||||
TUYA_DEVICE,
|
TUYA_DEVICES,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -57,11 +57,6 @@ async def async_setup_entry(
|
|||||||
This is a generic method and each platform should lock domain and
|
This is a generic method and each platform should lock domain and
|
||||||
entity_class with functools.partial.
|
entity_class with functools.partial.
|
||||||
"""
|
"""
|
||||||
print("ASYNC_SETUP_ENTRY: {} {} {}".format(
|
|
||||||
config_entry.data,
|
|
||||||
entity_class,
|
|
||||||
flow_schema(None).items())
|
|
||||||
)
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for dev_id in config_entry.data[CONF_DEVICES]:
|
for dev_id in config_entry.data[CONF_DEVICES]:
|
||||||
@@ -77,7 +72,7 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
if len(entities_to_setup) > 0:
|
if len(entities_to_setup) > 0:
|
||||||
|
|
||||||
tuyainterface = hass.data[DOMAIN][dev_id][TUYA_DEVICE]
|
tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id]
|
||||||
|
|
||||||
dps_config_fields = list(get_dps_for_platform(flow_schema))
|
dps_config_fields = list(get_dps_for_platform(flow_schema))
|
||||||
|
|
||||||
@@ -94,8 +89,6 @@ async def async_setup_entry(
|
|||||||
entity_config[CONF_ID],
|
entity_config[CONF_ID],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print("ADDING {} entities".format(len(entities)))
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
@@ -209,11 +202,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
"New local key for %s: from %s to %s",
|
"New local key for %s: from %s to %s",
|
||||||
dev_id,
|
dev_id,
|
||||||
old_key,
|
old_key,
|
||||||
self._local_key
|
self._local_key,
|
||||||
)
|
)
|
||||||
self.exception(
|
self.exception(
|
||||||
f"Connect to {self._config_entry[CONF_HOST]} failed: %s",
|
f"Connect to {self._config_entry[CONF_HOST]} failed: %s", type(e)
|
||||||
type(e)
|
|
||||||
)
|
)
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
await self._interface.close()
|
await self._interface.close()
|
||||||
@@ -243,8 +235,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
if self._disconnect_task is not None:
|
if self._disconnect_task is not None:
|
||||||
self._disconnect_task()
|
self._disconnect_task()
|
||||||
self.debug(
|
self.debug(
|
||||||
"Closed connection with device %s.",
|
"Closed connection with device %s.", self._config_entry[CONF_FRIENDLY_NAME]
|
||||||
self._config_entry[CONF_FRIENDLY_NAME]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def set_dp(self, state, dp_index):
|
async def set_dp(self, state, dp_index):
|
||||||
|
@@ -21,7 +21,7 @@ from homeassistant.const import (
|
|||||||
CONF_CLIENT_ID,
|
CONF_CLIENT_ID,
|
||||||
CONF_CLIENT_SECRET,
|
CONF_CLIENT_SECRET,
|
||||||
CONF_REGION,
|
CONF_REGION,
|
||||||
|
CONF_USERNAME,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
PLATFORM_TO_ADD = "platform_to_add"
|
PLATFORM_TO_ADD = "platform_to_add"
|
||||||
NO_ADDITIONAL_ENTITIES = "no_additional_entities"
|
NO_ADDITIONAL_ENTITIES = "no_additional_entities"
|
||||||
DISCOVERED_DEVICE = "discovered_device"
|
SELECTED_DEVICE = "selected_device"
|
||||||
|
|
||||||
CUSTOM_DEVICE = "..."
|
CUSTOM_DEVICE = "..."
|
||||||
|
|
||||||
@@ -71,10 +71,11 @@ CLOUD_SETUP_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||||
vol.Required(CONF_CLIENT_SECRET): cv.string,
|
vol.Required(CONF_CLIENT_SECRET): cv.string,
|
||||||
vol.Required(CONF_USER_ID): cv.string,
|
vol.Required(CONF_USER_ID): cv.string,
|
||||||
|
vol.Optional(CONF_USERNAME, default=DOMAIN): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
BASIC_INFO_SCHEMA = vol.Schema(
|
CONFIGURE_DEVICE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_FRIENDLY_NAME): str,
|
vol.Required(CONF_FRIENDLY_NAME): str,
|
||||||
vol.Required(CONF_LOCAL_KEY): str,
|
vol.Required(CONF_LOCAL_KEY): str,
|
||||||
@@ -101,16 +102,17 @@ PICK_ENTITY_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def devices_schema(discovered_devices, cloud_devices_list):
|
def devices_schema(discovered_devices, cloud_devices_list, add_custom_device=True):
|
||||||
"""Create schema for devices step."""
|
"""Create schema for devices step."""
|
||||||
devices = {}
|
devices = {}
|
||||||
for dev_id, dev in discovered_devices.items():
|
for dev_id, dev_host in discovered_devices.items():
|
||||||
dev_name = dev_id
|
dev_name = dev_id
|
||||||
if dev_id in cloud_devices_list.keys():
|
if dev_id in cloud_devices_list.keys():
|
||||||
dev_name = cloud_devices_list[dev_id][CONF_NAME]
|
dev_name = cloud_devices_list[dev_id][CONF_NAME]
|
||||||
devices[dev_id] = f"{dev_name} ({discovered_devices[dev_id]['ip']})"
|
devices[dev_id] = f"{dev_name} ({dev_host})"
|
||||||
|
|
||||||
devices.update({CUSTOM_DEVICE: CUSTOM_DEVICE})
|
if add_custom_device:
|
||||||
|
devices.update({CUSTOM_DEVICE: CUSTOM_DEVICE})
|
||||||
|
|
||||||
# devices.update(
|
# devices.update(
|
||||||
# {
|
# {
|
||||||
@@ -118,15 +120,13 @@ def devices_schema(discovered_devices, cloud_devices_list):
|
|||||||
# for ent in entries
|
# for ent in entries
|
||||||
# }
|
# }
|
||||||
# )
|
# )
|
||||||
return vol.Schema(
|
return vol.Schema({vol.Required(SELECTED_DEVICE): vol.In(devices)})
|
||||||
{vol.Required(DISCOVERED_DEVICE): vol.In(devices)}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def options_schema(entities):
|
def options_schema(entities):
|
||||||
"""Create schema for options."""
|
"""Create schema for options."""
|
||||||
entity_names = [
|
entity_names = [
|
||||||
f"{entity[CONF_ID]} {entity[CONF_FRIENDLY_NAME]}" for entity in entities
|
f"{entity[CONF_ID]}: {entity[CONF_FRIENDLY_NAME]}" for entity in entities
|
||||||
]
|
]
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
{
|
{
|
||||||
@@ -258,7 +258,7 @@ async def attempt_cloud_connection(hass, user_input):
|
|||||||
user_input.get(CONF_REGION),
|
user_input.get(CONF_REGION),
|
||||||
user_input.get(CONF_CLIENT_ID),
|
user_input.get(CONF_CLIENT_ID),
|
||||||
user_input.get(CONF_CLIENT_SECRET),
|
user_input.get(CONF_CLIENT_SECRET),
|
||||||
user_input.get(CONF_USER_ID)
|
user_input.get(CONF_USER_ID),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = await tuya_api.async_get_access_token()
|
res = await tuya_api.async_get_access_token()
|
||||||
@@ -306,17 +306,18 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
placeholders = {"msg": res["msg"]}
|
placeholders = {"msg": res["msg"]}
|
||||||
|
|
||||||
defaults = {}
|
defaults = {}
|
||||||
defaults[CONF_REGION] = 'eu'
|
defaults[CONF_REGION] = "eu"
|
||||||
defaults[CONF_CLIENT_ID] = 'xx'
|
defaults[CONF_CLIENT_ID] = "xxx"
|
||||||
defaults[CONF_CLIENT_SECRET] = 'xx'
|
defaults[CONF_CLIENT_SECRET] = "xxx"
|
||||||
defaults[CONF_USER_ID] = 'xx'
|
defaults[CONF_USER_ID] = "xxx"
|
||||||
|
defaults[CONF_USERNAME] = "xxx"
|
||||||
defaults.update(user_input or {})
|
defaults.update(user_input or {})
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults),
|
data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
description_placeholders=placeholders
|
description_placeholders=placeholders,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _create_entry(self, user_input):
|
async def _create_entry(self, user_input):
|
||||||
@@ -331,13 +332,15 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
user_input[CONF_DEVICES] = {}
|
user_input[CONF_DEVICES] = {}
|
||||||
|
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title="LocalTuya",
|
title=user_input.get(CONF_USERNAME),
|
||||||
data=user_input,
|
data=user_input,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_import(self, user_input):
|
async def async_step_import(self, user_input):
|
||||||
"""Handle import from YAML."""
|
"""Handle import from YAML."""
|
||||||
_LOGGER.error("Configuration via YAML file is no longer supported by this integration.")
|
_LOGGER.error(
|
||||||
|
"Configuration via YAML file is no longer supported by this integration."
|
||||||
|
)
|
||||||
# await self.async_set_unique_id(user_input[CONF_DEVICE_ID])
|
# await self.async_set_unique_id(user_input[CONF_DEVICE_ID])
|
||||||
# self._abort_if_unique_id_configured(updates=user_input)
|
# self._abort_if_unique_id_configured(updates=user_input)
|
||||||
# return self.async_create_entry(
|
# return self.async_create_entry(
|
||||||
@@ -354,12 +357,12 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
# self.dps_strings = config_entry.data.get(CONF_DPS_STRINGS, gen_dps_strings())
|
# self.dps_strings = config_entry.data.get(CONF_DPS_STRINGS, gen_dps_strings())
|
||||||
# self.entities = config_entry.data[CONF_ENTITIES]
|
# self.entities = config_entry.data[CONF_ENTITIES]
|
||||||
self.selected_device = None
|
self.selected_device = None
|
||||||
self.basic_info = None
|
self.editing_device = False
|
||||||
|
self.device_data = None
|
||||||
self.dps_strings = []
|
self.dps_strings = []
|
||||||
self.selected_platform = None
|
self.selected_platform = None
|
||||||
self.devices = {}
|
self.discovered_devices = {}
|
||||||
self.entities = []
|
self.entities = []
|
||||||
self.data = None
|
|
||||||
|
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
"""Manage basic options."""
|
"""Manage basic options."""
|
||||||
@@ -369,6 +372,8 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
return await self.async_step_cloud_setup()
|
return await self.async_step_cloud_setup()
|
||||||
if user_input.get(CONF_ACTION) == CONF_ADD_DEVICE:
|
if user_input.get(CONF_ACTION) == CONF_ADD_DEVICE:
|
||||||
return await self.async_step_add_device()
|
return await self.async_step_add_device()
|
||||||
|
if user_input.get(CONF_ACTION) == CONF_EDIT_DEVICE:
|
||||||
|
return await self.async_step_edit_device()
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="init",
|
step_id="init",
|
||||||
@@ -403,27 +408,28 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
step_id="cloud_setup",
|
step_id="cloud_setup",
|
||||||
data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults),
|
data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
description_placeholders=placeholders
|
description_placeholders=placeholders,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_add_device(self, user_input=None):
|
async def async_step_add_device(self, user_input=None):
|
||||||
"""Handle adding a new device."""
|
"""Handle adding a new device."""
|
||||||
# Use cache if available or fallback to manual discovery
|
# Use cache if available or fallback to manual discovery
|
||||||
|
self.editing_device = False
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("Selected {}".format(user_input))
|
print("Selected {}".format(user_input))
|
||||||
if user_input[DISCOVERED_DEVICE] != CUSTOM_DEVICE:
|
if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE:
|
||||||
self.selected_device = user_input[DISCOVERED_DEVICE]
|
self.selected_device = user_input[SELECTED_DEVICE]
|
||||||
return await self.async_step_basic_info()
|
return await self.async_step_configure_device()
|
||||||
|
|
||||||
discovered_devices = {}
|
self.discovered_devices = {}
|
||||||
data = self.hass.data.get(DOMAIN)
|
data = self.hass.data.get(DOMAIN)
|
||||||
|
|
||||||
if data and DATA_DISCOVERY in data:
|
if data and DATA_DISCOVERY in data:
|
||||||
discovered_devices = data[DATA_DISCOVERY].devices
|
self.discovered_devices = data[DATA_DISCOVERY].devices
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
discovered_devices = await discover()
|
self.discovered_devices = await discover()
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
if ex.errno == errno.EADDRINUSE:
|
if ex.errno == errno.EADDRINUSE:
|
||||||
errors["base"] = "address_in_use"
|
errors["base"] = "address_in_use"
|
||||||
@@ -433,37 +439,90 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
_LOGGER.exception("discovery failed")
|
_LOGGER.exception("discovery failed")
|
||||||
errors["base"] = "discovery_failed"
|
errors["base"] = "discovery_failed"
|
||||||
|
|
||||||
self.devices = {
|
devices = {
|
||||||
ip: dev
|
dev_id: dev["ip"]
|
||||||
for ip, dev in discovered_devices.items()
|
for dev_id, dev in self.discovered_devices.items()
|
||||||
if dev["gwId"] not in self.config_entry.data[CONF_DEVICES]
|
if dev["gwId"] not in self.config_entry.data[CONF_DEVICES]
|
||||||
}
|
}
|
||||||
|
print("SCHEMA DEVS {}".format(devices))
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="add_device",
|
step_id="add_device",
|
||||||
data_schema=devices_schema(
|
data_schema=devices_schema(
|
||||||
self.devices,
|
devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list
|
||||||
self.hass.data[DOMAIN][DATA_CLOUD]._device_list
|
|
||||||
),
|
),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_basic_info(self, user_input=None):
|
async def async_step_edit_device(self, user_input=None):
|
||||||
|
"""Handle editing a device."""
|
||||||
|
self.editing_device = True
|
||||||
|
# Use cache if available or fallback to manual discovery
|
||||||
|
print("AAA")
|
||||||
|
errors = {}
|
||||||
|
if user_input is not None:
|
||||||
|
print("Selected {}".format(user_input))
|
||||||
|
self.selected_device = user_input[SELECTED_DEVICE]
|
||||||
|
dev_conf = self.config_entry.data[CONF_DEVICES][self.selected_device]
|
||||||
|
self.dps_strings = dev_conf.get(CONF_DPS_STRINGS, gen_dps_strings())
|
||||||
|
self.entities = dev_conf[CONF_ENTITIES]
|
||||||
|
print("Selected DPS {} ENT {}".format(self.dps_strings, self.entities))
|
||||||
|
|
||||||
|
return await self.async_step_configure_device()
|
||||||
|
|
||||||
|
print("BBB: {}".format(self.config_entry.data[CONF_DEVICES]))
|
||||||
|
devices = {}
|
||||||
|
for dev_id, configured_dev in self.config_entry.data[CONF_DEVICES].items():
|
||||||
|
devices[dev_id] = configured_dev[CONF_HOST]
|
||||||
|
print("SCHEMA DEVS {}".format(devices))
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="edit_device",
|
||||||
|
data_schema=devices_schema(
|
||||||
|
devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list, False
|
||||||
|
),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_configure_device(self, user_input=None):
|
||||||
"""Handle input of basic info."""
|
"""Handle input of basic info."""
|
||||||
errors = {}
|
errors = {}
|
||||||
dev_id = self.selected_device
|
dev_id = self.selected_device
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID))
|
print("INPUT1!! {} {}".format(user_input, dev_id))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.basic_info = user_input
|
self.device_data = user_input.copy()
|
||||||
if dev_id is not None:
|
if dev_id is not None:
|
||||||
# self.basic_info[CONF_PRODUCT_KEY] = self.devices[
|
# self.device_data[CONF_PRODUCT_KEY] = self.devices[
|
||||||
# self.selected_device
|
# self.selected_device
|
||||||
# ]["productKey"]
|
# ]["productKey"]
|
||||||
cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list
|
cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list
|
||||||
if dev_id in cloud_devs:
|
if dev_id in cloud_devs:
|
||||||
self.basic_info[CONF_MODEL] = cloud_devs[dev_id].get(CONF_PRODUCT_NAME)
|
self.device_data[CONF_MODEL] = cloud_devs[dev_id].get(
|
||||||
|
CONF_PRODUCT_NAME
|
||||||
|
)
|
||||||
|
if self.editing_device:
|
||||||
|
self.device_data.update(
|
||||||
|
{
|
||||||
|
CONF_DEVICE_ID: dev_id,
|
||||||
|
CONF_MODEL: self.device_data[CONF_MODEL],
|
||||||
|
CONF_DPS_STRINGS: self.dps_strings,
|
||||||
|
CONF_ENTITIES: [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if len(user_input[CONF_ENTITIES]) > 0:
|
||||||
|
entity_ids = [
|
||||||
|
int(entity.split(":")[0])
|
||||||
|
for entity in user_input[CONF_ENTITIES]
|
||||||
|
]
|
||||||
|
device_config = self.config_entry.data[CONF_DEVICES][dev_id]
|
||||||
|
self.entities = [
|
||||||
|
entity
|
||||||
|
for entity in device_config[CONF_ENTITIES]
|
||||||
|
if entity[CONF_ID] in entity_ids
|
||||||
|
]
|
||||||
|
return await self.async_step_configure_entity()
|
||||||
|
|
||||||
self.dps_strings = await validate_input(self.hass, user_input)
|
self.dps_strings = await validate_input(self.hass, user_input)
|
||||||
print("ZIO KEN!! {} ".format(self.dps_strings))
|
print("ZIO KEN!! {} ".format(self.dps_strings))
|
||||||
@@ -478,21 +537,19 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
_LOGGER.exception("Unexpected exception")
|
_LOGGER.exception("Unexpected exception")
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
|
|
||||||
# If selected device exists as a config entry, load config from it
|
|
||||||
if self.selected_device in self.config_entry.data[CONF_DEVICES]:
|
|
||||||
print("ALREADY EXISTING!! {}".format(self.selected_device))
|
|
||||||
# entry = self.config_entry.data[CONF_DEVICES][self.selected_device]
|
|
||||||
# await self.async_set_unique_id(entry.data[CONF_DEVICE_ID])
|
|
||||||
# self.basic_info = entry.data.copy()
|
|
||||||
# self.dps_strings = self.basic_info.pop(CONF_DPS_STRINGS).copy()
|
|
||||||
# self.entities = self.basic_info.pop(CONF_ENTITIES).copy()
|
|
||||||
# return await self.async_step_pick_entity_type()
|
|
||||||
|
|
||||||
# Insert default values from discovery and cloud if present
|
|
||||||
defaults = {}
|
defaults = {}
|
||||||
defaults.update(user_input or {})
|
if self.editing_device:
|
||||||
if dev_id is not None:
|
# If selected device exists as a config entry, load config from it
|
||||||
device = self.devices[dev_id]
|
print("ALREADY EXISTING!! {}".format(dev_id))
|
||||||
|
defaults = self.config_entry.data[CONF_DEVICES][dev_id].copy()
|
||||||
|
print("ALREADY EXISTING!! {} {}".format(self.entities, defaults))
|
||||||
|
schema = schema_defaults(options_schema(self.entities), **defaults)
|
||||||
|
placeholders = {"for_device": f" for device `{dev_id}`"}
|
||||||
|
print("SCHEMA!! {}".format(schema))
|
||||||
|
elif dev_id is not None:
|
||||||
|
# Insert default values from discovery and cloud if present
|
||||||
|
print("NEW DEVICE!! {}".format(dev_id))
|
||||||
|
device = self.discovered_devices[dev_id]
|
||||||
defaults[CONF_HOST] = device.get("ip")
|
defaults[CONF_HOST] = device.get("ip")
|
||||||
defaults[CONF_DEVICE_ID] = device.get("gwId")
|
defaults[CONF_DEVICE_ID] = device.get("gwId")
|
||||||
defaults[CONF_PROTOCOL_VERSION] = device.get("version")
|
defaults[CONF_PROTOCOL_VERSION] = device.get("version")
|
||||||
@@ -500,29 +557,100 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
if dev_id in cloud_devs:
|
if dev_id in cloud_devs:
|
||||||
defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY)
|
defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY)
|
||||||
defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME)
|
defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME)
|
||||||
|
schema = schema_defaults(CONFIGURE_DEVICE_SCHEMA, **defaults)
|
||||||
|
placeholders = {"for_device": ""}
|
||||||
|
defaults.update(user_input or {})
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="basic_info",
|
step_id="configure_device",
|
||||||
data_schema=schema_defaults(BASIC_INFO_SCHEMA, **defaults),
|
data_schema=schema,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
|
description_placeholders=placeholders,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_pick_entity_type(self, user_input=None):
|
||||||
|
"""Handle asking if user wants to add another entity."""
|
||||||
|
if user_input is not None:
|
||||||
|
print("INPUT4!! {} {}".format(user_input, self.device_data))
|
||||||
|
if user_input.get(NO_ADDITIONAL_ENTITIES):
|
||||||
|
config = {
|
||||||
|
**self.device_data,
|
||||||
|
CONF_DPS_STRINGS: self.dps_strings,
|
||||||
|
CONF_ENTITIES: self.entities,
|
||||||
|
}
|
||||||
|
print("NEW CONFIG!! {}".format(config))
|
||||||
|
|
||||||
|
# entry = async_config_entry_by_device_id(self.hass, self.unique_id)
|
||||||
|
dev_id = self.device_data.get(CONF_DEVICE_ID)
|
||||||
|
if dev_id in self.config_entry.data[CONF_DEVICES]:
|
||||||
|
print("AGGIORNO !! {}".format(dev_id))
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
self.config_entry, data=config
|
||||||
|
)
|
||||||
|
return self.async_abort(
|
||||||
|
reason="device_success",
|
||||||
|
description_placeholders={
|
||||||
|
"dev_name": config.get(CONF_FRIENDLY_NAME),
|
||||||
|
"action": "updated",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
print("CREO NUOVO DEVICE!! {}".format(dev_id))
|
||||||
|
new_data = self.config_entry.data.copy()
|
||||||
|
print("PRE: {}".format(new_data))
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
|
# new_data[CONF_DEVICES]["AZZ"] = "OK"
|
||||||
|
new_data[CONF_DEVICES].update({dev_id: config})
|
||||||
|
print("POST: {}".format(new_data))
|
||||||
|
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
self.config_entry,
|
||||||
|
data=new_data,
|
||||||
|
)
|
||||||
|
return self.async_create_entry(title="", data={})
|
||||||
|
self.async_create_entry(title="", data={})
|
||||||
|
|
||||||
|
self.selected_platform = user_input[PLATFORM_TO_ADD]
|
||||||
|
return await self.async_step_configure_entity()
|
||||||
|
|
||||||
|
# Add a checkbox that allows bailing out from config flow if at least one
|
||||||
|
# entity has been added
|
||||||
|
schema = PICK_ENTITY_SCHEMA
|
||||||
|
if self.selected_platform is not None:
|
||||||
|
schema = schema.extend(
|
||||||
|
{vol.Required(NO_ADDITIONAL_ENTITIES, default=True): bool}
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(step_id="pick_entity_type", data_schema=schema)
|
||||||
|
|
||||||
|
def available_dps_strings(self):
|
||||||
|
available_dps = []
|
||||||
|
# print("FILTERING!! {} {}".format(self.dps_strings, self.entities))
|
||||||
|
used_dps = [str(entity[CONF_ID]) for entity in self.entities]
|
||||||
|
# print("FILTERING-- {} {}".format(self.dps_strings, used_dps))
|
||||||
|
for dp_string in self.dps_strings:
|
||||||
|
dp = dp_string.split(" ")[0]
|
||||||
|
# print("FILTERING2!! {} {}".format(dp_string, dp))
|
||||||
|
if dp not in used_dps:
|
||||||
|
available_dps.append(dp_string)
|
||||||
|
return available_dps
|
||||||
|
|
||||||
async def async_step_entity(self, user_input=None):
|
async def async_step_entity(self, user_input=None):
|
||||||
"""Manage entity settings."""
|
"""Manage entity settings."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID))
|
print("INPUT2!! {} {}".format(user_input, CONF_DEVICE_ID))
|
||||||
print("ZIO KEN!! {} ".format(self.dps_strings))
|
print("ZIO KEN!! {} ".format(self.dps_strings))
|
||||||
entity = strip_dps_values(user_input, self.dps_strings)
|
entity = strip_dps_values(user_input, self.dps_strings)
|
||||||
entity[CONF_ID] = self.current_entity[CONF_ID]
|
entity[CONF_ID] = self.current_entity[CONF_ID]
|
||||||
entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM]
|
entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM]
|
||||||
self.data[CONF_ENTITIES].append(entity)
|
self.device_data[CONF_ENTITIES].append(entity)
|
||||||
|
|
||||||
if len(self.entities) == len(self.data[CONF_ENTITIES]):
|
if len(self.entities) == len(self.device_data[CONF_ENTITIES]):
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.config_entry,
|
self.config_entry,
|
||||||
title=self.data[CONF_FRIENDLY_NAME],
|
title=self.device_data[CONF_FRIENDLY_NAME],
|
||||||
data=self.data,
|
data=self.device_data,
|
||||||
)
|
)
|
||||||
return self.async_create_entry(title="", data={})
|
return self.async_create_entry(title="", data={})
|
||||||
|
|
||||||
@@ -541,102 +669,56 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_pick_entity_type(self, user_input=None):
|
async def async_step_configure_entity(self, user_input=None):
|
||||||
"""Handle asking if user wants to add another entity."""
|
"""Manage entity settings."""
|
||||||
if user_input is not None:
|
|
||||||
print("INPUT3!! {} {}".format(user_input, self.basic_info))
|
|
||||||
print("AAAZIO KEN!! {} ".format(self.dps_strings))
|
|
||||||
print("NAAA!! {} ".format(user_input.get(NO_ADDITIONAL_ENTITIES)))
|
|
||||||
if user_input.get(NO_ADDITIONAL_ENTITIES):
|
|
||||||
print("INPUT4!! {}".format(self.dps_strings))
|
|
||||||
print("INPUT4!! {}".format(self.entities))
|
|
||||||
config = {
|
|
||||||
**self.basic_info,
|
|
||||||
CONF_DPS_STRINGS: self.dps_strings,
|
|
||||||
CONF_ENTITIES: self.entities,
|
|
||||||
}
|
|
||||||
print("NEW CONFIG!! {}".format(config))
|
|
||||||
# self.config_entry.data[CONF_DEVICES]
|
|
||||||
|
|
||||||
entry = async_config_entry_by_device_id(self.hass, self.unique_id)
|
|
||||||
dev_id = self.basic_info.get(CONF_DEVICE_ID)
|
|
||||||
if dev_id in self.config_entry.data[CONF_DEVICES]:
|
|
||||||
print("AGGIORNO !! {}".format(dev_id))
|
|
||||||
self.hass.config_entries.async_update_entry(self.config_entry, data=config)
|
|
||||||
return self.async_abort(
|
|
||||||
reason="device_success",
|
|
||||||
description_placeholders={
|
|
||||||
"dev_name": config.get(CONF_FRIENDLY_NAME),
|
|
||||||
"action": "updated"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print("CREO NUOVO DEVICE!! {}".format(dev_id))
|
|
||||||
new_data = self.config_entry.data.copy()
|
|
||||||
print("PRE: {}".format(new_data))
|
|
||||||
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
|
||||||
# new_data[CONF_DEVICES]["AZZ"] = "OK"
|
|
||||||
new_data[CONF_DEVICES].update({dev_id: config})
|
|
||||||
print("POST: {}".format(new_data))
|
|
||||||
|
|
||||||
self.hass.config_entries.async_update_entry(
|
|
||||||
self.config_entry,
|
|
||||||
data=new_data,
|
|
||||||
)
|
|
||||||
return self.async_create_entry(title="", data={})
|
|
||||||
self.async_create_entry(title="", data={})
|
|
||||||
print("DONE! now message {}".format(new_data))
|
|
||||||
return self.async_abort(
|
|
||||||
reason="device_success",
|
|
||||||
description_placeholders={
|
|
||||||
"dev_name": config.get(CONF_FRIENDLY_NAME),
|
|
||||||
"action": "created"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# return self.async_create_entry(
|
|
||||||
# title=config[CONF_FRIENDLY_NAME], data=config
|
|
||||||
# )
|
|
||||||
print("MA ZZZIO KEN!! {} ".format(self.dps_strings))
|
|
||||||
|
|
||||||
self.selected_platform = user_input[PLATFORM_TO_ADD]
|
|
||||||
return await self.async_step_add_entity()
|
|
||||||
|
|
||||||
# Add a checkbox that allows bailing out from config flow if at least one
|
|
||||||
# entity has been added
|
|
||||||
schema = PICK_ENTITY_SCHEMA
|
|
||||||
if self.selected_platform is not None:
|
|
||||||
schema = schema.extend(
|
|
||||||
{vol.Required(NO_ADDITIONAL_ENTITIES, default=True): bool}
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.async_show_form(step_id="pick_entity_type", data_schema=schema)
|
|
||||||
|
|
||||||
async def async_step_add_entity(self, user_input=None):
|
|
||||||
"""Handle adding a new entity."""
|
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID))
|
print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID))
|
||||||
already_configured = any(
|
already_configured = any(
|
||||||
switch[CONF_ID] == int(user_input[CONF_ID].split(" ")[0])
|
entity[CONF_ID] == int(user_input[CONF_ID].split(" ")[0])
|
||||||
for switch in self.entities
|
for entity in self.entities
|
||||||
)
|
)
|
||||||
if not already_configured:
|
if not already_configured:
|
||||||
user_input[CONF_PLATFORM] = self.selected_platform
|
user_input[CONF_PLATFORM] = self.selected_platform
|
||||||
self.entities.append(strip_dps_values(user_input, self.dps_strings))
|
self.entities.append(strip_dps_values(user_input, self.dps_strings))
|
||||||
return await self.async_step_pick_entity_type()
|
# new entity added. Let's check if there are more left...
|
||||||
|
print("ADDED. Remaining... {}".format(self.available_dps_strings()))
|
||||||
|
user_input = None
|
||||||
|
if len(self.available_dps_strings()) == 0:
|
||||||
|
user_input = {NO_ADDITIONAL_ENTITIES: True}
|
||||||
|
return await self.async_step_pick_entity_type(user_input)
|
||||||
|
|
||||||
errors["base"] = "entity_already_configured"
|
errors["base"] = "entity_already_configured"
|
||||||
|
|
||||||
|
if self.editing_device:
|
||||||
|
schema = platform_schema(
|
||||||
|
self.current_entity[CONF_PLATFORM], self.dps_strings, allow_id=False
|
||||||
|
)
|
||||||
|
schema = schema_defaults(schema, self.dps_strings, **self.current_entity)
|
||||||
|
placeholders = {
|
||||||
|
"entity": f"entity with DP {self.current_entity[CONF_ID]}",
|
||||||
|
"platform": self.current_entity[CONF_PLATFORM],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
available_dps = self.available_dps_strings()
|
||||||
|
schema = platform_schema(self.selected_platform, available_dps)
|
||||||
|
placeholders = {
|
||||||
|
"entity": "an entity",
|
||||||
|
"platform": self.selected_platform,
|
||||||
|
}
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="add_entity",
|
step_id="configure_entity",
|
||||||
data_schema=platform_schema(self.selected_platform, self.dps_strings),
|
data_schema=schema,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
description_placeholders={"platform": self.selected_platform},
|
description_placeholders=placeholders,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_yaml_import(self, user_input=None):
|
async def async_step_yaml_import(self, user_input=None):
|
||||||
"""Manage YAML imports."""
|
"""Manage YAML imports."""
|
||||||
_LOGGER.error("Configuration via YAML file is no longer supported by this integration.")
|
_LOGGER.error(
|
||||||
|
"Configuration via YAML file is no longer supported by this integration."
|
||||||
|
)
|
||||||
# if user_input is not None:
|
# if user_input is not None:
|
||||||
# return self.async_create_entry(title="", data={})
|
# return self.async_create_entry(title="", data={})
|
||||||
# return self.async_show_form(step_id="yaml_import")
|
# return self.async_show_form(step_id="yaml_import")
|
||||||
@@ -644,7 +726,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
@property
|
@property
|
||||||
def current_entity(self):
|
def current_entity(self):
|
||||||
"""Existing configuration for entity currently being edited."""
|
"""Existing configuration for entity currently being edited."""
|
||||||
return self.entities[len(self.data[CONF_ENTITIES])]
|
return self.entities[len(self.device_data[CONF_ENTITIES])]
|
||||||
|
|
||||||
|
|
||||||
class CannotConnect(exceptions.HomeAssistantError):
|
class CannotConnect(exceptions.HomeAssistantError):
|
||||||
|
@@ -19,7 +19,7 @@ PLATFORMS = [
|
|||||||
"vacuum",
|
"vacuum",
|
||||||
]
|
]
|
||||||
|
|
||||||
TUYA_DEVICE = "tuya_device"
|
TUYA_DEVICES = "tuya_devices"
|
||||||
|
|
||||||
ATTR_CURRENT = "current"
|
ATTR_CURRENT = "current"
|
||||||
ATTR_CURRENT_CONSUMPTION = "current_consumption"
|
ATTR_CURRENT_CONSUMPTION = "current_consumption"
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "localtuya",
|
"domain": "localtuya",
|
||||||
"name": "LocalTuya integration",
|
"name": "LocalTuya integration",
|
||||||
"version": "3.2.1",
|
"version": "4.0.0",
|
||||||
"documentation": "https://github.com/rospogrigio/localtuya/",
|
"documentation": "https://github.com/rospogrigio/localtuya/",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
|
@@ -23,7 +23,8 @@
|
|||||||
"region": "API server region",
|
"region": "API server region",
|
||||||
"client_id": "Client ID",
|
"client_id": "Client ID",
|
||||||
"client_secret": "Secret",
|
"client_secret": "Secret",
|
||||||
"user_id": "User ID"
|
"user_id": "User ID",
|
||||||
|
"user_name": "Username"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,19 +63,27 @@
|
|||||||
"title": "Add a new device",
|
"title": "Add a new device",
|
||||||
"description": "Pick one of the automatically discovered devices or `...` to manually to add a device.",
|
"description": "Pick one of the automatically discovered devices or `...` to manually to add a device.",
|
||||||
"data": {
|
"data": {
|
||||||
"discovered_device": "Discovered Devices"
|
"selected_device": "Discovered Devices"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"basic_info": {
|
"edit_device": {
|
||||||
"title": "Add Tuya device",
|
"title": "Edit a new device",
|
||||||
"description": "Fill in the basic device details. The name entered here will be used to identify the device itself (as seen in the `Integrations` page). You will add entities and give them names in the following steps.",
|
"description": "Pick the configured device you wish to edit.",
|
||||||
|
"data": {
|
||||||
|
"selected_device": "Configured Devices"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configure_device": {
|
||||||
|
"title": "Configure Tuya device",
|
||||||
|
"description": "Fill in the device details{for_device}.",
|
||||||
"data": {
|
"data": {
|
||||||
"friendly_name": "Name",
|
"friendly_name": "Name",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"device_id": "Device ID",
|
"device_id": "Device ID",
|
||||||
"local_key": "Local key",
|
"local_key": "Local key",
|
||||||
"protocol_version": "Protocol Version",
|
"protocol_version": "Protocol Version",
|
||||||
"scan_interval": "Scan interval (seconds, only when not updating automatically)"
|
"scan_interval": "Scan interval (seconds, only when not updating automatically)",
|
||||||
|
"entities": "Entities (uncheck an entity to remove it)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pick_entity_type": {
|
"pick_entity_type": {
|
||||||
@@ -85,9 +94,9 @@
|
|||||||
"no_additional_entities": "Do not add any more entities"
|
"no_additional_entities": "Do not add any more entities"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"add_entity": {
|
"configure_entity": {
|
||||||
"title": "Add new entity",
|
"title": "Configure entity",
|
||||||
"description": "Please fill out the details for an entity with type `{platform}`. All settings except for `ID` can be changed from the Options page later.",
|
"description": "Please fill out the details for {entity} with type `{platform}`. All settings except for `ID` can be changed from the Options page later.",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"friendly_name": "Friendly name",
|
"friendly_name": "Friendly name",
|
||||||
|
Reference in New Issue
Block a user