Options flow patch (apply on top of lock-cycle-transitions.patch)
This commit is contained in:
@@ -0,0 +1,270 @@
|
||||
diff --git a/custom_components/tuya_local_ble/__init__.py b/custom_components/tuya_local_ble/__init__.py
|
||||
index d000ccd..d1a060b 100644
|
||||
--- a/custom_components/tuya_local_ble/__init__.py
|
||||
+++ b/custom_components/tuya_local_ble/__init__.py
|
||||
@@ -89,7 +89,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
- #entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||
+ entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||
|
||||
async def _async_stop(event: Event) -> None:
|
||||
"""Close the connection."""
|
||||
@@ -100,6 +100,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
return True
|
||||
|
||||
+async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
+ """Reload the entry when options change."""
|
||||
+ await hass.config_entries.async_reload(entry.entry_id)
|
||||
+
|
||||
+
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
diff --git a/custom_components/tuya_local_ble/config_flow.py b/custom_components/tuya_local_ble/config_flow.py
|
||||
index a89eff8..d93947a 100644
|
||||
--- a/custom_components/tuya_local_ble/config_flow.py
|
||||
+++ b/custom_components/tuya_local_ble/config_flow.py
|
||||
@@ -8,8 +8,10 @@ from typing import Any
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
+ ConfigEntry,
|
||||
ConfigFlow,
|
||||
- ConfigFlowResult
|
||||
+ ConfigFlowResult,
|
||||
+ OptionsFlow,
|
||||
)
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfoBleak,
|
||||
@@ -18,22 +20,45 @@ from homeassistant.components.bluetooth import (
|
||||
from homeassistant.const import CONF_ADDRESS
|
||||
from homeassistant.core import callback
|
||||
#from homeassistant.data_entry_flow import FlowResult
|
||||
+from homeassistant.helpers.selector import (
|
||||
+ NumberSelector,
|
||||
+ NumberSelectorConfig,
|
||||
+ NumberSelectorMode,
|
||||
+ TextSelector,
|
||||
+ TextSelectorConfig,
|
||||
+)
|
||||
|
||||
from .tuya_ble import SERVICE_UUID, TuyaBLEDeviceCredentials
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
+ CONF_BLE_UNLOCK_CHECK,
|
||||
+ CONF_LOCK_CYCLE_SECONDS,
|
||||
+ CONF_UNLOCK_CYCLE_SECONDS,
|
||||
)
|
||||
from .devices import TuyaBLEData, get_device_readable_name
|
||||
from .keyman import HASSTuyaBLEDeviceManager
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
+OPTIONS_KEYS = (
|
||||
+ CONF_LOCK_CYCLE_SECONDS,
|
||||
+ CONF_UNLOCK_CYCLE_SECONDS,
|
||||
+ CONF_BLE_UNLOCK_CHECK,
|
||||
+)
|
||||
+
|
||||
+
|
||||
class TuyaBLEConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Tuya BLE."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
+ @staticmethod
|
||||
+ @callback
|
||||
+ def async_get_options_flow(config_entry: ConfigEntry) -> TuyaBLEOptionsFlow:
|
||||
+ """Get the options flow for this handler."""
|
||||
+ return TuyaBLEOptionsFlow()
|
||||
+
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
super().__init__()
|
||||
@@ -132,3 +157,61 @@ class TuyaBLEConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
+
|
||||
+
|
||||
+class TuyaBLEOptionsFlow(OptionsFlow):
|
||||
+ """Handle options for a Tuya BLE device.
|
||||
+
|
||||
+ Values set here override the matching fields in devices.json; an empty
|
||||
+ field falls back to devices.json (or the built-in default).
|
||||
+ """
|
||||
+
|
||||
+ async def async_step_init(
|
||||
+ self, user_input: dict[str, Any] | None = None
|
||||
+ ) -> ConfigFlowResult:
|
||||
+ """Manage the device options."""
|
||||
+ if user_input is not None:
|
||||
+ options = dict(self.config_entry.options)
|
||||
+ for key in OPTIONS_KEYS:
|
||||
+ value = user_input.get(key)
|
||||
+ if value in (None, ""):
|
||||
+ options.pop(key, None)
|
||||
+ else:
|
||||
+ options[key] = value
|
||||
+ return self.async_create_entry(title="", data=options)
|
||||
+
|
||||
+ options = self.config_entry.options
|
||||
+ cycle_selector = NumberSelector(
|
||||
+ NumberSelectorConfig(
|
||||
+ min=1,
|
||||
+ max=120,
|
||||
+ step=0.1,
|
||||
+ unit_of_measurement="s",
|
||||
+ mode=NumberSelectorMode.BOX,
|
||||
+ )
|
||||
+ )
|
||||
+ return self.async_show_form(
|
||||
+ step_id="init",
|
||||
+ data_schema=vol.Schema(
|
||||
+ {
|
||||
+ vol.Optional(
|
||||
+ CONF_LOCK_CYCLE_SECONDS,
|
||||
+ description={
|
||||
+ "suggested_value": options.get(CONF_LOCK_CYCLE_SECONDS)
|
||||
+ },
|
||||
+ ): cycle_selector,
|
||||
+ vol.Optional(
|
||||
+ CONF_UNLOCK_CYCLE_SECONDS,
|
||||
+ description={
|
||||
+ "suggested_value": options.get(CONF_UNLOCK_CYCLE_SECONDS)
|
||||
+ },
|
||||
+ ): cycle_selector,
|
||||
+ vol.Optional(
|
||||
+ CONF_BLE_UNLOCK_CHECK,
|
||||
+ description={
|
||||
+ "suggested_value": options.get(CONF_BLE_UNLOCK_CHECK)
|
||||
+ },
|
||||
+ ): TextSelector(TextSelectorConfig()),
|
||||
+ },
|
||||
+ ),
|
||||
+ )
|
||||
diff --git a/custom_components/tuya_local_ble/keyman.py b/custom_components/tuya_local_ble/keyman.py
|
||||
index ea2d4ef..02e4e73 100644
|
||||
--- a/custom_components/tuya_local_ble/keyman.py
|
||||
+++ b/custom_components/tuya_local_ble/keyman.py
|
||||
@@ -84,6 +84,15 @@ class HASSTuyaBLEDeviceManager(AbstaractTuyaBLEDeviceManager):
|
||||
credentials = self._devicedata.get(address)
|
||||
|
||||
if credentials:
|
||||
+ # Options set via the integration UI override devices.json values.
|
||||
+ for key in (
|
||||
+ CONF_BLE_UNLOCK_CHECK,
|
||||
+ CONF_LOCK_CYCLE_SECONDS,
|
||||
+ CONF_UNLOCK_CYCLE_SECONDS,
|
||||
+ ):
|
||||
+ value = self._data.get(key)
|
||||
+ if value not in (None, ""):
|
||||
+ credentials = {**credentials, key: value}
|
||||
result = TuyaBLEDeviceCredentials(
|
||||
credentials.get(CONF_UUID, ""),
|
||||
credentials.get(CONF_LOCAL_KEY, ""),
|
||||
diff --git a/custom_components/tuya_local_ble/strings.json b/custom_components/tuya_local_ble/strings.json
|
||||
index 76f3fb2..ef92e87 100644
|
||||
--- a/custom_components/tuya_local_ble/strings.json
|
||||
+++ b/custom_components/tuya_local_ble/strings.json
|
||||
@@ -210,5 +210,23 @@
|
||||
"name": "Program: position[/time];..."
|
||||
}
|
||||
}
|
||||
+ },
|
||||
+ "options": {
|
||||
+ "step": {
|
||||
+ "init": {
|
||||
+ "title": "Tuya Local BLE options",
|
||||
+ "description": "These values override the matching fields in config/tuya_local_ble/devices.json. Leave a field empty to fall back to devices.json (or the built-in default). If the device stops working after a change, clear the fields here or fix devices.json directly.",
|
||||
+ "data": {
|
||||
+ "lock_cycle_seconds": "Lock cycle time (s)",
|
||||
+ "unlock_cycle_seconds": "Unlock cycle time (s)",
|
||||
+ "ble_unlock_check": "ble_unlock_check (base64)"
|
||||
+ },
|
||||
+ "data_description": {
|
||||
+ "lock_cycle_seconds": "How long the motor runs when locking, including the pause at the end stop. New commands are rejected while the cycle is running.",
|
||||
+ "unlock_cycle_seconds": "Same as above, for unlocking.",
|
||||
+ "ble_unlock_check": "Raw Tuya status value used to authorize remote unlock. Refresh it from the Tuya IoT cloud (Get Status Reporting Log) if unlock starts returning error code 1."
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
diff --git a/custom_components/tuya_local_ble/translations/en.json b/custom_components/tuya_local_ble/translations/en.json
|
||||
index 019f00e..68d63af 100644
|
||||
--- a/custom_components/tuya_local_ble/translations/en.json
|
||||
+++ b/custom_components/tuya_local_ble/translations/en.json
|
||||
@@ -209,6 +209,23 @@
|
||||
"name": "Program: position[/time];..."
|
||||
}
|
||||
}
|
||||
+ },
|
||||
+ "options": {
|
||||
+ "step": {
|
||||
+ "init": {
|
||||
+ "title": "Tuya Local BLE options",
|
||||
+ "description": "These values override the matching fields in config/tuya_local_ble/devices.json. Leave a field empty to fall back to devices.json (or the built-in default). If the device stops working after a change, clear the fields here or fix devices.json directly.",
|
||||
+ "data": {
|
||||
+ "lock_cycle_seconds": "Lock cycle time (s)",
|
||||
+ "unlock_cycle_seconds": "Unlock cycle time (s)",
|
||||
+ "ble_unlock_check": "ble_unlock_check (base64)"
|
||||
+ },
|
||||
+ "data_description": {
|
||||
+ "lock_cycle_seconds": "How long the motor runs when locking, including the pause at the end stop. New commands are rejected while the cycle is running.",
|
||||
+ "unlock_cycle_seconds": "Same as above, for unlocking.",
|
||||
+ "ble_unlock_check": "Raw Tuya status value used to authorize remote unlock. Refresh it from the Tuya IoT cloud (Get Status Reporting Log) if unlock starts returning error code 1."
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
-
|
||||
\ No newline at end of file
|
||||
+}
|
||||
diff --git a/custom_components/tuya_local_ble/translations/ru.json b/custom_components/tuya_local_ble/translations/ru.json
|
||||
new file mode 100644
|
||||
index 0000000..e976df9
|
||||
--- /dev/null
|
||||
+++ b/custom_components/tuya_local_ble/translations/ru.json
|
||||
@@ -0,0 +1,37 @@
|
||||
+{
|
||||
+ "config": {
|
||||
+ "abort": {
|
||||
+ "no_unconfigured_devices": "Ненастроенные устройства не найдены."
|
||||
+ },
|
||||
+ "error": {
|
||||
+ "device_not_registered": "Устройство не зарегистрировано, проверьте конфигурацию."
|
||||
+ },
|
||||
+ "flow_title": "{name}",
|
||||
+ "step": {
|
||||
+ "device": {
|
||||
+ "data": {
|
||||
+ "address": "Устройство Tuya BLE"
|
||||
+ },
|
||||
+ "description": "Выберите устройство Tuya BLE для настройки. Устройство должно быть зарегистрировано в облаке через мобильное приложение."
|
||||
+ }
|
||||
+ }
|
||||
+ },
|
||||
+ "options": {
|
||||
+ "step": {
|
||||
+ "init": {
|
||||
+ "title": "Настройки Tuya Local BLE",
|
||||
+ "description": "Эти значения перекрывают одноимённые поля в config/tuya_local_ble/devices.json. Пустое поле — используется значение из devices.json (или встроенное по умолчанию). Если после изменения устройство перестало работать — очистите поля здесь или поправьте devices.json напрямую.",
|
||||
+ "data": {
|
||||
+ "lock_cycle_seconds": "Время цикла закрытия (с)",
|
||||
+ "unlock_cycle_seconds": "Время цикла открытия (с)",
|
||||
+ "ble_unlock_check": "ble_unlock_check (base64)"
|
||||
+ },
|
||||
+ "data_description": {
|
||||
+ "lock_cycle_seconds": "Сколько работает мотор при закрытии, включая паузу после упора. Пока идёт цикл, повторные команды отклоняются.",
|
||||
+ "unlock_cycle_seconds": "То же самое, но для открытия.",
|
||||
+ "ble_unlock_check": "Сырое значение статуса Tuya для авторизации удалённого открытия. Если открытие начало возвращать error code 1 — обновите значение из облака Tuya (Get Status Reporting Log)."
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
Reference in New Issue
Block a user