Finalized device editing, device diagnostics, auto-update of local key and IP
This commit is contained in:
@@ -27,7 +27,6 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.reload import async_integration_yaml_config
|
|
||||||
|
|
||||||
from .cloud_api import TuyaCloudApi
|
from .cloud_api import TuyaCloudApi
|
||||||
from .common import TuyaDevice, async_config_entry_by_device_id
|
from .common import TuyaDevice, async_config_entry_by_device_id
|
||||||
@@ -73,13 +72,9 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
|
|
||||||
async def _handle_reload(service):
|
async def _handle_reload(service):
|
||||||
"""Handle reload service call."""
|
"""Handle reload service call."""
|
||||||
config = await async_integration_yaml_config(hass, DOMAIN)
|
_LOGGER.debug("Service %s.reload called: reloading integration", DOMAIN)
|
||||||
|
|
||||||
if not config or DOMAIN not in config:
|
|
||||||
return
|
|
||||||
|
|
||||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
# entries_by_id = {entry.data[CONF_DEVICE_ID]: entry for entry in current_entries}
|
|
||||||
|
|
||||||
reload_tasks = [
|
reload_tasks = [
|
||||||
hass.config_entries.async_reload(entry.entry_id)
|
hass.config_entries.async_reload(entry.entry_id)
|
||||||
@@ -107,42 +102,51 @@ async def async_setup(hass: HomeAssistant, config: dict):
|
|||||||
product_key = device["productKey"]
|
product_key = device["productKey"]
|
||||||
|
|
||||||
# If device is not in cache, check if a config entry exists
|
# If device is not in cache, check if a config entry exists
|
||||||
if device_id not in device_cache:
|
|
||||||
entry = async_config_entry_by_device_id(hass, device_id)
|
|
||||||
if entry:
|
|
||||||
# Save address from config entry in cache to trigger
|
|
||||||
# potential update below
|
|
||||||
device_cache[device_id] = entry.data[CONF_HOST]
|
|
||||||
|
|
||||||
if device_id not in device_cache:
|
|
||||||
return
|
|
||||||
|
|
||||||
entry = async_config_entry_by_device_id(hass, device_id)
|
entry = async_config_entry_by_device_id(hass, device_id)
|
||||||
if entry is None:
|
if entry is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
updates = {}
|
if device_id not in device_cache:
|
||||||
|
if entry and device_id in entry.data[CONF_DEVICES]:
|
||||||
|
# Save address from config entry in cache to trigger
|
||||||
|
# potential update below
|
||||||
|
host_ip = entry.data[CONF_DEVICES][device_id][CONF_HOST]
|
||||||
|
device_cache[device_id] = host_ip
|
||||||
|
|
||||||
|
if device_id not in device_cache:
|
||||||
|
return
|
||||||
|
|
||||||
|
dev_entry = entry.data[CONF_DEVICES][device_id]
|
||||||
|
|
||||||
|
new_data = entry.data.copy()
|
||||||
|
updated = False
|
||||||
|
|
||||||
if device_cache[device_id] != device_ip:
|
if device_cache[device_id] != device_ip:
|
||||||
updates[CONF_HOST] = device_ip
|
updated = True
|
||||||
|
new_data[CONF_DEVICES][device_id][CONF_HOST] = device_ip
|
||||||
device_cache[device_id] = device_ip
|
device_cache[device_id] = device_ip
|
||||||
|
|
||||||
if entry.data.get(CONF_PRODUCT_KEY) != product_key:
|
if dev_entry.get(CONF_PRODUCT_KEY) != product_key:
|
||||||
updates[CONF_PRODUCT_KEY] = product_key
|
updated = True
|
||||||
|
new_data[CONF_DEVICES][device_id][CONF_PRODUCT_KEY] = product_key
|
||||||
|
|
||||||
# Update settings if something changed, otherwise try to connect. Updating
|
# Update settings if something changed, otherwise try to connect. Updating
|
||||||
# settings triggers a reload of the config entry, which tears down the device
|
# settings triggers a reload of the config entry, which tears down the device
|
||||||
# so no need to connect in that case.
|
# so no need to connect in that case.
|
||||||
if updates:
|
if updated:
|
||||||
_LOGGER.debug("Update keys for device %s: %s", device_id, updates)
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
hass.config_entries.async_update_entry(
|
hass.config_entries.async_update_entry(
|
||||||
entry, data={**entry.data, **updates}
|
entry, data=new_data
|
||||||
)
|
)
|
||||||
elif entry.entry_id in hass.data[DOMAIN]:
|
device = hass.data[DOMAIN][TUYA_DEVICES][device_id]
|
||||||
_LOGGER.debug("Device %s found with IP %s", device_id, device_ip)
|
if not device.connected:
|
||||||
|
device.async_connect()
|
||||||
|
elif device_id in hass.data[DOMAIN][TUYA_DEVICES]:
|
||||||
|
# _LOGGER.debug("Device %s found with IP %s", device_id, device_ip)
|
||||||
|
|
||||||
device = hass.data[DOMAIN][TUYA_DEVICES][device_id]
|
device = hass.data[DOMAIN][TUYA_DEVICES][device_id]
|
||||||
device.async_connect()
|
if not device.connected:
|
||||||
|
device.async_connect()
|
||||||
|
|
||||||
discovery = TuyaDiscovery(_device_discovered)
|
discovery = TuyaDiscovery(_device_discovered)
|
||||||
|
|
||||||
@@ -159,18 +163,11 @@ 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 device in hass.data[DOMAIN][TUYA_DEVICES]:
|
for device_id, device in hass.data[DOMAIN][TUYA_DEVICES].items():
|
||||||
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)
|
||||||
|
|
||||||
hass.helpers.service.async_register_admin_service(
|
hass.helpers.service.async_register_admin_service(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@@ -205,6 +202,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry):
|
|||||||
new_data[CONF_DEVICES] = {
|
new_data[CONF_DEVICES] = {
|
||||||
config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()
|
config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()
|
||||||
}
|
}
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
config_entry.version = new_version
|
config_entry.version = new_version
|
||||||
hass.config_entries.async_update_entry(
|
hass.config_entries.async_update_entry(
|
||||||
config_entry, title=DOMAIN, data=new_data
|
config_entry, title=DOMAIN, data=new_data
|
||||||
@@ -217,6 +215,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry):
|
|||||||
new_data[CONF_DEVICES].update(
|
new_data[CONF_DEVICES].update(
|
||||||
{config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()}
|
{config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()}
|
||||||
)
|
)
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
hass.config_entries.async_update_entry(stored_entries[0], data=new_data)
|
hass.config_entries.async_update_entry(stored_entries[0], data=new_data)
|
||||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
|
|
||||||
@@ -241,11 +240,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
user_id = entry.data[CONF_USER_ID]
|
user_id = entry.data[CONF_USER_ID]
|
||||||
tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id)
|
tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id)
|
||||||
res = await tuya_api.async_get_access_token()
|
res = await tuya_api.async_get_access_token()
|
||||||
_LOGGER.debug("ACCESS TOKEN RES: %s . CLOUD DEVICES:", res)
|
if res != "ok":
|
||||||
|
_LOGGER.error("Cloud API connection failed: %s", res)
|
||||||
|
_LOGGER.info("Cloud API connection succeeded.")
|
||||||
res = await tuya_api.async_get_devices_list()
|
res = await tuya_api.async_get_devices_list()
|
||||||
for dev_id, dev in tuya_api._device_list.items():
|
for dev_id, dev in tuya_api._device_list.items():
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Name: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"]
|
"Cloud device: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"]
|
||||||
)
|
)
|
||||||
hass.data[DOMAIN][DATA_CLOUD] = tuya_api
|
hass.data[DOMAIN][DATA_CLOUD] = tuya_api
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@ 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, entry, dev_id)
|
||||||
hass.data[DOMAIN][TUYA_DEVICES][dev_id] = device
|
hass.data[DOMAIN][TUYA_DEVICES][dev_id] = 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])
|
||||||
|
@@ -67,7 +67,7 @@ class TuyaCloudApi:
|
|||||||
"sign_method": "HMAC-SHA256",
|
"sign_method": "HMAC-SHA256",
|
||||||
}
|
}
|
||||||
full_url = self._base_url + url
|
full_url = self._base_url + url
|
||||||
print("\n" + method + ": [{}]".format(full_url))
|
# print("\n" + method + ": [{}]".format(full_url))
|
||||||
|
|
||||||
if method == "GET":
|
if method == "GET":
|
||||||
func = functools.partial(
|
func = functools.partial(
|
||||||
@@ -80,7 +80,7 @@ class TuyaCloudApi:
|
|||||||
headers=dict(default_par, **headers),
|
headers=dict(default_par, **headers),
|
||||||
data=json.dumps(body),
|
data=json.dumps(body),
|
||||||
)
|
)
|
||||||
print("BODY: [{}]".format(body))
|
# print("BODY: [{}]".format(body))
|
||||||
elif method == "PUT":
|
elif method == "PUT":
|
||||||
func = functools.partial(
|
func = functools.partial(
|
||||||
requests.put,
|
requests.put,
|
||||||
@@ -104,9 +104,7 @@ class TuyaCloudApi:
|
|||||||
if not r_json["success"]:
|
if not r_json["success"]:
|
||||||
return f"Error {r_json['code']}: {r_json['msg']}"
|
return f"Error {r_json['code']}: {r_json['msg']}"
|
||||||
|
|
||||||
print(r.json())
|
|
||||||
self._access_token = r.json()["result"]["access_token"]
|
self._access_token = r.json()["result"]["access_token"]
|
||||||
print("GET_ACCESS_TOKEN: {}".format(self._access_token))
|
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
async def async_get_devices_list(self):
|
async def async_get_devices_list(self):
|
||||||
@@ -120,11 +118,11 @@ class TuyaCloudApi:
|
|||||||
|
|
||||||
r_json = r.json()
|
r_json = r.json()
|
||||||
if not r_json["success"]:
|
if not r_json["success"]:
|
||||||
print(
|
# print(
|
||||||
"Request failed, reply is {}".format(
|
# "Request failed, reply is {}".format(
|
||||||
json.dumps(r_json, indent=2, ensure_ascii=False)
|
# json.dumps(r_json, indent=2, ensure_ascii=False)
|
||||||
)
|
# )
|
||||||
)
|
# )
|
||||||
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"]}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import time
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
@@ -24,6 +25,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
|||||||
|
|
||||||
from . import pytuya
|
from . import pytuya
|
||||||
from .const import (
|
from .const import (
|
||||||
|
ATTR_UPDATED_AT,
|
||||||
CONF_LOCAL_KEY,
|
CONF_LOCAL_KEY,
|
||||||
CONF_PROTOCOL_VERSION,
|
CONF_PROTOCOL_VERSION,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@@ -112,7 +114,7 @@ def async_config_entry_by_device_id(hass, device_id):
|
|||||||
"""Look up config entry by device id."""
|
"""Look up config entry by device id."""
|
||||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
for entry in current_entries:
|
for entry in current_entries:
|
||||||
if entry.data[CONF_DEVICE_ID] == device_id:
|
if device_id in entry.data[CONF_DEVICES]:
|
||||||
return entry
|
return entry
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -120,11 +122,12 @@ def async_config_entry_by_device_id(hass, device_id):
|
|||||||
class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
||||||
"""Cache wrapper for pytuya.TuyaInterface."""
|
"""Cache wrapper for pytuya.TuyaInterface."""
|
||||||
|
|
||||||
def __init__(self, hass, config_entry):
|
def __init__(self, hass, config_entry, dev_id):
|
||||||
"""Initialize the cache."""
|
"""Initialize the cache."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._config_entry = config_entry
|
self._config_entry = config_entry
|
||||||
|
self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id]
|
||||||
self._interface = None
|
self._interface = None
|
||||||
self._status = {}
|
self._status = {}
|
||||||
self.dps_to_request = {}
|
self.dps_to_request = {}
|
||||||
@@ -132,11 +135,11 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
self._disconnect_task = None
|
self._disconnect_task = None
|
||||||
self._unsub_interval = None
|
self._unsub_interval = None
|
||||||
self._local_key = self._config_entry[CONF_LOCAL_KEY]
|
self._local_key = self._dev_config_entry[CONF_LOCAL_KEY]
|
||||||
self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID])
|
self.set_logger(_LOGGER, self._dev_config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
# This has to be done in case the device type is type_0d
|
# This has to be done in case the device type is type_0d
|
||||||
for entity in config_entry[CONF_ENTITIES]:
|
for entity in self._dev_config_entry[CONF_ENTITIES]:
|
||||||
self.dps_to_request[entity[CONF_ID]] = None
|
self.dps_to_request[entity[CONF_ID]] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -151,14 +154,14 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
|
|
||||||
async def _make_connection(self):
|
async def _make_connection(self):
|
||||||
"""Subscribe localtuya entity events."""
|
"""Subscribe localtuya entity events."""
|
||||||
self.debug("Connecting to %s", self._config_entry[CONF_HOST])
|
self.debug("Connecting to %s", self._dev_config_entry[CONF_HOST])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._interface = await pytuya.connect(
|
self._interface = await pytuya.connect(
|
||||||
self._config_entry[CONF_HOST],
|
self._dev_config_entry[CONF_HOST],
|
||||||
self._config_entry[CONF_DEVICE_ID],
|
self._dev_config_entry[CONF_DEVICE_ID],
|
||||||
self._local_key,
|
self._local_key,
|
||||||
float(self._config_entry[CONF_PROTOCOL_VERSION]),
|
float(self._dev_config_entry[CONF_PROTOCOL_VERSION]),
|
||||||
self,
|
self,
|
||||||
)
|
)
|
||||||
self._interface.add_dps_to_request(self.dps_to_request)
|
self._interface.add_dps_to_request(self.dps_to_request)
|
||||||
@@ -174,52 +177,63 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self.debug(
|
self.debug(
|
||||||
"New entity %s was added to %s",
|
"New entity %s was added to %s",
|
||||||
entity_id,
|
entity_id,
|
||||||
self._config_entry[CONF_HOST],
|
self._dev_config_entry[CONF_HOST],
|
||||||
)
|
)
|
||||||
self._dispatch_status()
|
self._dispatch_status()
|
||||||
|
|
||||||
signal = f"localtuya_entity_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_entity_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
self._disconnect_task = async_dispatcher_connect(
|
self._disconnect_task = async_dispatcher_connect(
|
||||||
self._hass, signal, _new_entity_handler
|
self._hass, signal, _new_entity_handler
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
CONF_SCAN_INTERVAL in self._config_entry
|
CONF_SCAN_INTERVAL in self._dev_config_entry
|
||||||
and self._config_entry[CONF_SCAN_INTERVAL] > 0
|
and self._dev_config_entry[CONF_SCAN_INTERVAL] > 0
|
||||||
):
|
):
|
||||||
self._unsub_interval = async_track_time_interval(
|
self._unsub_interval = async_track_time_interval(
|
||||||
self._hass,
|
self._hass,
|
||||||
self._async_refresh,
|
self._async_refresh,
|
||||||
timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]),
|
timedelta(seconds=self._dev_config_entry[CONF_SCAN_INTERVAL]),
|
||||||
)
|
)
|
||||||
except UnicodeDecodeError as e: # pylint: disable=broad-except
|
except UnicodeDecodeError as e: # pylint: disable=broad-except
|
||||||
dev_id = self._config_entry[CONF_DEVICE_ID]
|
|
||||||
cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list
|
|
||||||
if dev_id in cloud_devs:
|
|
||||||
old_key = self._local_key
|
|
||||||
self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY)
|
|
||||||
self.error(
|
|
||||||
"New local key for %s: from %s to %s",
|
|
||||||
dev_id,
|
|
||||||
old_key,
|
|
||||||
self._local_key,
|
|
||||||
)
|
|
||||||
self.exception(
|
self.exception(
|
||||||
f"Connect to {self._config_entry[CONF_HOST]} failed: %s", type(e)
|
f"Connect to {self._dev_config_entry[CONF_HOST]} failed: %s", type(e)
|
||||||
)
|
)
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
await self._interface.close()
|
await self._interface.close()
|
||||||
self._interface = None
|
self._interface = None
|
||||||
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
except Exception as e: # pylint: disable=broad-except
|
||||||
self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed")
|
self.exception(f"Connect to {self._dev_config_entry[CONF_HOST]} failed")
|
||||||
self.error("BBBB: %s", type(e))
|
self.error("BBBB: %s", type(type(e)))
|
||||||
|
if 'json.decode' in str(type(e)):
|
||||||
|
self.error("BBBB2")
|
||||||
|
await self.update_local_key()
|
||||||
|
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
await self._interface.close()
|
await self._interface.close()
|
||||||
self._interface = None
|
self._interface = None
|
||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
|
|
||||||
|
async def update_local_key(self):
|
||||||
|
dev_id = self._dev_config_entry[CONF_DEVICE_ID]
|
||||||
|
await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list()
|
||||||
|
cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list
|
||||||
|
if dev_id in cloud_devs:
|
||||||
|
old_key = self._local_key
|
||||||
|
self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY)
|
||||||
|
new_data = self._config_entry.data.copy()
|
||||||
|
new_data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = self._local_key
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
|
self._hass.config_entries.async_update_entry(
|
||||||
|
self._config_entry,
|
||||||
|
data=new_data,
|
||||||
|
)
|
||||||
|
self.debug(
|
||||||
|
"New local key for %s: from %s to %s",
|
||||||
|
dev_id, old_key, self._local_key
|
||||||
|
)
|
||||||
|
|
||||||
async def _async_refresh(self, _now):
|
async def _async_refresh(self, _now):
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
await self._interface.update_dps()
|
await self._interface.update_dps()
|
||||||
@@ -235,7 +249,8 @@ 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.", self._config_entry[CONF_FRIENDLY_NAME]
|
"Closed connection with device %s.",
|
||||||
|
self._dev_config_entry[CONF_FRIENDLY_NAME]
|
||||||
)
|
)
|
||||||
|
|
||||||
async def set_dp(self, state, dp_index):
|
async def set_dp(self, state, dp_index):
|
||||||
@@ -247,7 +262,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self.exception("Failed to set DP %d to %d", dp_index, state)
|
self.exception("Failed to set DP %d to %d", dp_index, state)
|
||||||
else:
|
else:
|
||||||
self.error(
|
self.error(
|
||||||
"Not connected to device %s", self._config_entry[CONF_FRIENDLY_NAME]
|
"Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME]
|
||||||
)
|
)
|
||||||
|
|
||||||
async def set_dps(self, states):
|
async def set_dps(self, states):
|
||||||
@@ -259,7 +274,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self.exception("Failed to set DPs %r", states)
|
self.exception("Failed to set DPs %r", states)
|
||||||
else:
|
else:
|
||||||
self.error(
|
self.error(
|
||||||
"Not connected to device %s", self._config_entry[CONF_FRIENDLY_NAME]
|
"Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME]
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@@ -269,13 +284,13 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._dispatch_status()
|
self._dispatch_status()
|
||||||
|
|
||||||
def _dispatch_status(self):
|
def _dispatch_status(self):
|
||||||
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self._hass, signal, self._status)
|
async_dispatcher_send(self._hass, signal, self._status)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def disconnected(self):
|
def disconnected(self):
|
||||||
"""Device disconnected."""
|
"""Device disconnected."""
|
||||||
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self._hass, signal, None)
|
async_dispatcher_send(self._hass, signal, None)
|
||||||
if self._unsub_interval is not None:
|
if self._unsub_interval is not None:
|
||||||
self._unsub_interval()
|
self._unsub_interval()
|
||||||
@@ -291,11 +306,11 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
"""Initialize the Tuya entity."""
|
"""Initialize the Tuya entity."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._device = device
|
self._device = device
|
||||||
self._config_entry = config_entry
|
self._dev_config_entry = config_entry
|
||||||
self._config = get_entity_config(config_entry, dp_id)
|
self._config = get_entity_config(config_entry, dp_id)
|
||||||
self._dp_id = dp_id
|
self._dp_id = dp_id
|
||||||
self._status = {}
|
self._status = {}
|
||||||
self.set_logger(logger, self._config_entry[CONF_DEVICE_ID])
|
self.set_logger(logger, self._dev_config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe localtuya events."""
|
"""Subscribe localtuya events."""
|
||||||
@@ -317,28 +332,28 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
self.status_updated()
|
self.status_updated()
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
|
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(self.hass, signal, _update_handler)
|
async_dispatcher_connect(self.hass, signal, _update_handler)
|
||||||
)
|
)
|
||||||
|
|
||||||
signal = f"localtuya_entity_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_entity_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self.hass, signal, self.entity_id)
|
async_dispatcher_send(self.hass, signal, self.entity_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return device information for the device registry."""
|
"""Return device information for the device registry."""
|
||||||
model = self._config_entry.get(CONF_MODEL, "Tuya generic")
|
model = self._dev_config_entry.get(CONF_MODEL, "Tuya generic")
|
||||||
return {
|
return {
|
||||||
"identifiers": {
|
"identifiers": {
|
||||||
# Serial numbers are unique identifiers within a specific domain
|
# Serial numbers are unique identifiers within a specific domain
|
||||||
(DOMAIN, f"local_{self._config_entry[CONF_DEVICE_ID]}")
|
(DOMAIN, f"local_{self._dev_config_entry[CONF_DEVICE_ID]}")
|
||||||
},
|
},
|
||||||
"name": self._config_entry[CONF_FRIENDLY_NAME],
|
"name": self._dev_config_entry[CONF_FRIENDLY_NAME],
|
||||||
"manufacturer": "Tuya",
|
"manufacturer": "Tuya",
|
||||||
"model": f"{model} ({self._config_entry[CONF_DEVICE_ID]})",
|
"model": f"{model} ({self._dev_config_entry[CONF_DEVICE_ID]})",
|
||||||
"sw_version": self._config_entry[CONF_PROTOCOL_VERSION],
|
"sw_version": self._dev_config_entry[CONF_PROTOCOL_VERSION],
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -354,7 +369,7 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return unique device identifier."""
|
"""Return unique device identifier."""
|
||||||
return f"local_{self._config_entry[CONF_DEVICE_ID]}_{self._dp_id}"
|
return f"local_{self._dev_config_entry[CONF_DEVICE_ID]}_{self._dp_id}"
|
||||||
|
|
||||||
def has_config(self, attr):
|
def has_config(self, attr):
|
||||||
"""Return if a config parameter has a valid value."""
|
"""Return if a config parameter has a valid value."""
|
||||||
|
@@ -5,6 +5,7 @@ import time
|
|||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
import homeassistant.helpers.entity_registry as er
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant import config_entries, core, exceptions
|
from homeassistant import config_entries, core, exceptions
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@@ -253,7 +254,7 @@ async def validate_input(hass: core.HomeAssistant, data):
|
|||||||
|
|
||||||
async def attempt_cloud_connection(hass, user_input):
|
async def attempt_cloud_connection(hass, user_input):
|
||||||
"""Create device."""
|
"""Create device."""
|
||||||
tuya_api = TuyaCloudApi(
|
cloud_api = TuyaCloudApi(
|
||||||
hass,
|
hass,
|
||||||
user_input.get(CONF_REGION),
|
user_input.get(CONF_REGION),
|
||||||
user_input.get(CONF_CLIENT_ID),
|
user_input.get(CONF_CLIENT_ID),
|
||||||
@@ -261,20 +262,18 @@ async def attempt_cloud_connection(hass, user_input):
|
|||||||
user_input.get(CONF_USER_ID),
|
user_input.get(CONF_USER_ID),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = await tuya_api.async_get_access_token()
|
res = await cloud_api.async_get_access_token()
|
||||||
_LOGGER.debug("ACCESS TOKEN RES: %s", res)
|
|
||||||
if res != "ok":
|
if res != "ok":
|
||||||
return {"reason": "authentication_failed", "msg": res}
|
_LOGGER.error("Cloud API connection failed: %s", res)
|
||||||
|
return cloud_api, {"reason": "authentication_failed", "msg": res}
|
||||||
|
|
||||||
res = await tuya_api.async_get_devices_list()
|
res = await cloud_api.async_get_devices_list()
|
||||||
_LOGGER.debug("DEV LIST RES: %s", res)
|
|
||||||
if res != "ok":
|
if res != "ok":
|
||||||
return {"reason": "device_list_failed", "msg": res}
|
_LOGGER.error("Cloud API get_devices_list failed: %s", res)
|
||||||
|
return cloud_api, {"reason": "device_list_failed", "msg": res}
|
||||||
|
_LOGGER.info("Cloud API connection succeeded.")
|
||||||
|
|
||||||
for dev_id, dev in tuya_api._device_list.items():
|
return cloud_api, {}
|
||||||
print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ")
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
@@ -297,8 +296,7 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
errors = {}
|
errors = {}
|
||||||
placeholders = {}
|
placeholders = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("ECCOCI")
|
cloud_api, res = await attempt_cloud_connection(self.hass, user_input)
|
||||||
res = await attempt_cloud_connection(self.hass, user_input)
|
|
||||||
|
|
||||||
if len(res) == 0:
|
if len(res) == 0:
|
||||||
return await self._create_entry(user_input)
|
return await self._create_entry(user_input)
|
||||||
@@ -322,9 +320,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
async def _create_entry(self, user_input):
|
async def _create_entry(self, user_input):
|
||||||
"""Register new entry."""
|
"""Register new entry."""
|
||||||
# if not self.unique_id:
|
|
||||||
# await self.async_set_unique_id(password)
|
|
||||||
# self._abort_if_unique_id_configured()
|
|
||||||
if self._async_current_entries():
|
if self._async_current_entries():
|
||||||
return self.async_abort(reason="already_configured")
|
return self.async_abort(reason="already_configured")
|
||||||
|
|
||||||
@@ -341,11 +336,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Configuration via YAML file is no longer supported by this integration."
|
"Configuration via YAML file is no longer supported by this integration."
|
||||||
)
|
)
|
||||||
# await self.async_set_unique_id(user_input[CONF_DEVICE_ID])
|
|
||||||
# self._abort_if_unique_id_configured(updates=user_input)
|
|
||||||
# return self.async_create_entry(
|
|
||||||
# title=f"{user_input[CONF_FRIENDLY_NAME]} (YAML)", data=user_input
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
@@ -385,19 +375,24 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
errors = {}
|
errors = {}
|
||||||
placeholders = {}
|
placeholders = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
res = await attempt_cloud_connection(self.hass, user_input)
|
cloud_api, res = await attempt_cloud_connection(self.hass, user_input)
|
||||||
|
|
||||||
if len(res) == 0:
|
if len(res) == 0:
|
||||||
new_data = self.config_entry.data.copy()
|
new_data = self.config_entry.data.copy()
|
||||||
new_data.update(user_input)
|
new_data.update(user_input)
|
||||||
print("CURR_ENTRY {}".format(self.config_entry))
|
cloud_devs = cloud_api._device_list
|
||||||
print("NEW DATA {}".format(new_data))
|
for dev_id, dev in new_data[CONF_DEVICES].items():
|
||||||
|
if CONF_MODEL not in dev and dev_id in cloud_devs:
|
||||||
|
new_data[CONF_DEVICES][dev_id][CONF_MODEL] = cloud_devs[dev_id].get(
|
||||||
|
CONF_PRODUCT_NAME
|
||||||
|
)
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
|
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.config_entry,
|
self.config_entry,
|
||||||
data=new_data,
|
data=new_data,
|
||||||
)
|
)
|
||||||
return self.async_create_entry(title="", data={})
|
return self.async_create_entry(title=new_data.get(CONF_USERNAME), data={})
|
||||||
errors["base"] = res["reason"]
|
errors["base"] = res["reason"]
|
||||||
placeholders = {"msg": res["msg"]}
|
placeholders = {"msg": res["msg"]}
|
||||||
|
|
||||||
@@ -417,7 +412,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self.editing_device = False
|
self.editing_device = False
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("Selected {}".format(user_input))
|
|
||||||
if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE:
|
if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE:
|
||||||
self.selected_device = user_input[SELECTED_DEVICE]
|
self.selected_device = user_input[SELECTED_DEVICE]
|
||||||
return await self.async_step_configure_device()
|
return await self.async_step_configure_device()
|
||||||
@@ -444,7 +438,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
for dev_id, dev in self.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",
|
||||||
@@ -458,23 +451,18 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
"""Handle editing a device."""
|
"""Handle editing a device."""
|
||||||
self.editing_device = True
|
self.editing_device = True
|
||||||
# Use cache if available or fallback to manual discovery
|
# Use cache if available or fallback to manual discovery
|
||||||
print("AAA")
|
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("Selected {}".format(user_input))
|
|
||||||
self.selected_device = user_input[SELECTED_DEVICE]
|
self.selected_device = user_input[SELECTED_DEVICE]
|
||||||
dev_conf = self.config_entry.data[CONF_DEVICES][self.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.dps_strings = dev_conf.get(CONF_DPS_STRINGS, gen_dps_strings())
|
||||||
self.entities = dev_conf[CONF_ENTITIES]
|
self.entities = dev_conf[CONF_ENTITIES]
|
||||||
print("Selected DPS {} ENT {}".format(self.dps_strings, self.entities))
|
|
||||||
|
|
||||||
return await self.async_step_configure_device()
|
return await self.async_step_configure_device()
|
||||||
|
|
||||||
print("BBB: {}".format(self.config_entry.data[CONF_DEVICES]))
|
|
||||||
devices = {}
|
devices = {}
|
||||||
for dev_id, configured_dev in self.config_entry.data[CONF_DEVICES].items():
|
for dev_id, configured_dev in self.config_entry.data[CONF_DEVICES].items():
|
||||||
devices[dev_id] = configured_dev[CONF_HOST]
|
devices[dev_id] = configured_dev[CONF_HOST]
|
||||||
print("SCHEMA DEVS {}".format(devices))
|
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="edit_device",
|
step_id="edit_device",
|
||||||
@@ -489,8 +477,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
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("INPUT1!! {} {}".format(user_input, dev_id))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.device_data = user_input.copy()
|
self.device_data = user_input.copy()
|
||||||
if dev_id is not None:
|
if dev_id is not None:
|
||||||
@@ -525,7 +511,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
return await self.async_step_configure_entity()
|
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))
|
|
||||||
return await self.async_step_pick_entity_type()
|
return await self.async_step_pick_entity_type()
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
@@ -540,15 +525,11 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
defaults = {}
|
defaults = {}
|
||||||
if self.editing_device:
|
if self.editing_device:
|
||||||
# If selected device exists as a config entry, load config from it
|
# If selected device exists as a config entry, load config from it
|
||||||
print("ALREADY EXISTING!! {}".format(dev_id))
|
|
||||||
defaults = self.config_entry.data[CONF_DEVICES][dev_id].copy()
|
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)
|
schema = schema_defaults(options_schema(self.entities), **defaults)
|
||||||
placeholders = {"for_device": f" for device `{dev_id}`"}
|
placeholders = {"for_device": f" for device `{dev_id}`"}
|
||||||
print("SCHEMA!! {}".format(schema))
|
|
||||||
elif dev_id is not None:
|
elif dev_id is not None:
|
||||||
# Insert default values from discovery and cloud if present
|
# Insert default values from discovery and cloud if present
|
||||||
print("NEW DEVICE!! {}".format(dev_id))
|
|
||||||
device = self.discovered_devices[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")
|
||||||
@@ -571,19 +552,16 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
async def async_step_pick_entity_type(self, user_input=None):
|
async def async_step_pick_entity_type(self, user_input=None):
|
||||||
"""Handle asking if user wants to add another entity."""
|
"""Handle asking if user wants to add another entity."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("INPUT4!! {} {}".format(user_input, self.device_data))
|
|
||||||
if user_input.get(NO_ADDITIONAL_ENTITIES):
|
if user_input.get(NO_ADDITIONAL_ENTITIES):
|
||||||
config = {
|
config = {
|
||||||
**self.device_data,
|
**self.device_data,
|
||||||
CONF_DPS_STRINGS: self.dps_strings,
|
CONF_DPS_STRINGS: self.dps_strings,
|
||||||
CONF_ENTITIES: self.entities,
|
CONF_ENTITIES: self.entities,
|
||||||
}
|
}
|
||||||
print("NEW CONFIG!! {}".format(config))
|
|
||||||
|
|
||||||
# entry = async_config_entry_by_device_id(self.hass, self.unique_id)
|
# entry = async_config_entry_by_device_id(self.hass, self.unique_id)
|
||||||
dev_id = self.device_data.get(CONF_DEVICE_ID)
|
dev_id = self.device_data.get(CONF_DEVICE_ID)
|
||||||
if dev_id in self.config_entry.data[CONF_DEVICES]:
|
if dev_id in self.config_entry.data[CONF_DEVICES]:
|
||||||
print("AGGIORNO !! {}".format(dev_id))
|
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.config_entry, data=config
|
self.config_entry, data=config
|
||||||
)
|
)
|
||||||
@@ -595,13 +573,9 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
print("CREO NUOVO DEVICE!! {}".format(dev_id))
|
|
||||||
new_data = self.config_entry.data.copy()
|
new_data = self.config_entry.data.copy()
|
||||||
print("PRE: {}".format(new_data))
|
|
||||||
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
# new_data[CONF_DEVICES]["AZZ"] = "OK"
|
|
||||||
new_data[CONF_DEVICES].update({dev_id: config})
|
new_data[CONF_DEVICES].update({dev_id: config})
|
||||||
print("POST: {}".format(new_data))
|
|
||||||
|
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
self.config_entry,
|
self.config_entry,
|
||||||
@@ -625,12 +599,9 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
|
|
||||||
def available_dps_strings(self):
|
def available_dps_strings(self):
|
||||||
available_dps = []
|
available_dps = []
|
||||||
# print("FILTERING!! {} {}".format(self.dps_strings, self.entities))
|
|
||||||
used_dps = [str(entity[CONF_ID]) for entity in 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:
|
for dp_string in self.dps_strings:
|
||||||
dp = dp_string.split(" ")[0]
|
dp = dp_string.split(" ")[0]
|
||||||
# print("FILTERING2!! {} {}".format(dp_string, dp))
|
|
||||||
if dp not in used_dps:
|
if dp not in used_dps:
|
||||||
available_dps.append(dp_string)
|
available_dps.append(dp_string)
|
||||||
return available_dps
|
return available_dps
|
||||||
@@ -639,8 +610,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
"""Manage entity settings."""
|
"""Manage entity settings."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
print("INPUT2!! {} {}".format(user_input, CONF_DEVICE_ID))
|
|
||||||
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]
|
||||||
@@ -673,16 +642,38 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
"""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))
|
if self.editing_device:
|
||||||
already_configured = any(
|
entity = strip_dps_values(user_input, self.dps_strings)
|
||||||
entity[CONF_ID] == int(user_input[CONF_ID].split(" ")[0])
|
entity[CONF_ID] = self.current_entity[CONF_ID]
|
||||||
for entity in self.entities
|
entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM]
|
||||||
)
|
self.device_data[CONF_ENTITIES].append(entity)
|
||||||
if not already_configured:
|
|
||||||
|
if len(self.entities) == len(self.device_data[CONF_ENTITIES]):
|
||||||
|
# finished editing device. Let's store the new config entry....
|
||||||
|
dev_id = self.device_data[CONF_DEVICE_ID]
|
||||||
|
new_data = self.config_entry.data.copy()
|
||||||
|
entry_id = self.config_entry.entry_id
|
||||||
|
# removing entities from registry (they will be recreated)
|
||||||
|
ent_reg = await er.async_get_registry(self.hass)
|
||||||
|
reg_entities = {
|
||||||
|
ent.unique_id: ent.entity_id
|
||||||
|
for ent in er.async_entries_for_config_entry(ent_reg, entry_id)
|
||||||
|
if dev_id in ent.unique_id
|
||||||
|
}
|
||||||
|
for entity_id in reg_entities.values():
|
||||||
|
ent_reg.async_remove(entity_id)
|
||||||
|
|
||||||
|
new_data[CONF_DEVICES][dev_id] = self.device_data
|
||||||
|
new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000))
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
self.config_entry,
|
||||||
|
data=new_data,
|
||||||
|
)
|
||||||
|
return self.async_create_entry(title="", data={})
|
||||||
|
else:
|
||||||
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))
|
||||||
# new entity added. Let's check if there are more left...
|
# new entity added. Let's check if there are more left...
|
||||||
print("ADDED. Remaining... {}".format(self.available_dps_strings()))
|
|
||||||
user_input = None
|
user_input = None
|
||||||
if len(self.available_dps_strings()) == 0:
|
if len(self.available_dps_strings()) == 0:
|
||||||
user_input = {NO_ADDITIONAL_ENTITIES: True}
|
user_input = {NO_ADDITIONAL_ENTITIES: True}
|
||||||
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_DEVICES
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.device_registry import DeviceEntry
|
from homeassistant.helpers.device_registry import DeviceEntry
|
||||||
|
|
||||||
@@ -29,9 +30,11 @@ async def async_get_device_diagnostics(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
|
hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Return diagnostics for a device entry."""
|
"""Return diagnostics for a device entry."""
|
||||||
# dev_id = next(iter(device.identifiers))[1]
|
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
# data["device"] = device
|
dev_id = list(device.identifiers)[0][1].split("_")[-1]
|
||||||
# data["log"] = hass.data[DOMAIN][AIRBNK_DEVICES][dev_id].logger.retrieve_log()
|
data["device_config"] = entry.data[CONF_DEVICES][dev_id]
|
||||||
|
tuya_api = hass.data[DOMAIN][DATA_CLOUD]
|
||||||
|
if dev_id in tuya_api._device_list:
|
||||||
|
data["device_cloud_info"] = tuya_api._device_list[dev_id]
|
||||||
|
# data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log()
|
||||||
return data
|
return data
|
||||||
|
Reference in New Issue
Block a user