diff --git a/README.md b/README.md index 6d31173..c25d947 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,11 @@ Copy `tplink_easy_smart` folder from [latest release](https://github.com/vmakeev Configuration > [Integrations](https://my.home-assistant.io/redirect/integrations/) > Add Integration > [TP-Link Easy Smart](https://my.home-assistant.io/redirect/config_flow_start/?domain=tplink_easy_smart) +### Advanced options + +You can perform advanced component configuration by clicking the `CONFIGURE` button after adding it. Advanced settings include: +* Data update interval +* Enabling or disabling [port state switches](#port-state) ## Sensors @@ -97,7 +102,9 @@ _Note: The sensor will be unavailable if the port is not enabled (see [port stat The component allows you to enable and disable each port. -There are several switches that are always present: +By default, adding these switches is disabled, but you can add them via [options](#advanced-options). + +There are several switches: * `switch._port__enabled` -_Note: these switches are not enabled by default. If you need to use this feature, please enable it manually. Don't use this feature if you don't know what you are doing._ +_Note: don't use this feature if you don't know what you are doing._ diff --git a/custom_components/tplink_easy_smart/config_flow.py b/custom_components/tplink_easy_smart/config_flow.py index c5cf97b..11b18f4 100644 --- a/custom_components/tplink_easy_smart/config_flow.py +++ b/custom_components/tplink_easy_smart/config_flow.py @@ -4,7 +4,7 @@ import voluptuous as vol -from homeassistant.config_entries import CONN_CLASS_LOCAL_POLL, ConfigFlow +from homeassistant.config_entries import CONN_CLASS_LOCAL_POLL, ConfigFlow, OptionsFlow from homeassistant.const import ( CONF_HOST, CONF_NAME, @@ -19,10 +19,12 @@ from .client.coreapi import AuthenticationError, TpLinkWebApi from .const import ( + CONF_PORT_STATE_SWITCHES, DEFAULT_HOST, DEFAULT_NAME, DEFAULT_PASS, DEFAULT_PORT, + DEFAULT_PORT_STATE_SWITCHES, DEFAULT_SCAN_INTERVAL, DEFAULT_SSL, DEFAULT_USER, @@ -56,6 +58,12 @@ class TpLinkControllerConfigFlow(ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize.""" + @staticmethod + @callback + def async_get_options_flow(config_entry): + """Get the options flow for this handler.""" + return TpLinkControllerOptionsFlowHandler(config_entry) + async def async_step_import(self, user_input=None): """Occurs when a previous entry setup fails and is re-initiated.""" return await self.async_step_user(user_input) @@ -104,7 +112,6 @@ async def async_step_user(self, user_input=None): CONF_PORT: DEFAULT_PORT, CONF_SSL: DEFAULT_SSL, CONF_VERIFY_SSL: DEFAULT_VERIFY_SSL, - CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, }, errors=errors, ) @@ -122,15 +129,71 @@ def _show_config_form(self, user_input, errors=None): vol.Required(CONF_HOST, default=user_input[CONF_HOST]): str, vol.Required(CONF_USERNAME, default=user_input[CONF_USERNAME]): str, vol.Required(CONF_PASSWORD, default=user_input[CONF_PASSWORD]): str, - vol.Optional(CONF_PORT, default=user_input[CONF_PORT]): int, - vol.Optional(CONF_SSL, default=user_input[CONF_SSL]): bool, - vol.Optional( + vol.Required(CONF_PORT, default=user_input[CONF_PORT]): int, + vol.Required(CONF_SSL, default=user_input[CONF_SSL]): bool, + vol.Required( CONF_VERIFY_SSL, default=user_input[CONF_VERIFY_SSL] ): bool, - vol.Optional( - CONF_SCAN_INTERVAL, default=user_input[CONF_SCAN_INTERVAL] - ): int, } ), errors=errors, ) + + +# --------------------------- +# TpLinkControllerOptionsFlowHandler +# --------------------------- +class TpLinkControllerOptionsFlowHandler(OptionsFlow): + """Handle options.""" + + def __init__(self, config_entry): + """Initialize options flow.""" + self.config_entry = config_entry + self.options = dict(config_entry.options) + + async def async_step_init(self, user_input=None): + """Manage the options.""" + return await self.async_step_basic_options(user_input) + + async def async_step_basic_options(self, user_input=None): + """Manage the basic options options.""" + if user_input is not None: + self.options.update(user_input) + return await self.async_step_features_select() + + return self.async_show_form( + step_id="basic_options", + data_schema=vol.Schema( + { + vol.Required( + CONF_SCAN_INTERVAL, + default=self.config_entry.options.get( + CONF_SCAN_INTERVAL, + self.config_entry.data.get( + CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL + ), + ), + ): int, + } + ), + ) + + async def async_step_features_select(self, user_input=None): + """Manage the controls select options.""" + if user_input is not None: + self.options.update(user_input) + return self.async_create_entry(title="", data=self.options) + + return self.async_show_form( + step_id="features_select", + data_schema=vol.Schema( + { + vol.Required( + CONF_PORT_STATE_SWITCHES, + default=self.config_entry.options.get( + CONF_PORT_STATE_SWITCHES, DEFAULT_PORT_STATE_SWITCHES + ), + ): bool, + }, + ), + ) diff --git a/custom_components/tplink_easy_smart/const.py b/custom_components/tplink_easy_smart/const.py index 94a5781..8c333a1 100644 --- a/custom_components/tplink_easy_smart/const.py +++ b/custom_components/tplink_easy_smart/const.py @@ -14,6 +14,9 @@ DEFAULT_NAME: Final = "TP-Link Switch" DEFAULT_VERIFY_SSL: Final = False DEFAULT_SCAN_INTERVAL: Final = 30 +DEFAULT_PORT_STATE_SWITCHES: Final = False + +CONF_PORT_STATE_SWITCHES = "port_state_switches" DATA_KEY_COORDINATOR = "coordinator" diff --git a/custom_components/tplink_easy_smart/helpers.py b/custom_components/tplink_easy_smart/helpers.py index fd10106..520fe40 100644 --- a/custom_components/tplink_easy_smart/helpers.py +++ b/custom_components/tplink_easy_smart/helpers.py @@ -1,6 +1,7 @@ """Helpful common functions.""" from homeassistant.helpers.entity import generate_entity_id as hass_generate_id + from .update_coordinator import TpLinkDataUpdateCoordinator diff --git a/custom_components/tplink_easy_smart/manifest.json b/custom_components/tplink_easy_smart/manifest.json index 462edb7..2c9c286 100644 --- a/custom_components/tplink_easy_smart/manifest.json +++ b/custom_components/tplink_easy_smart/manifest.json @@ -11,6 +11,6 @@ "requirements": [ "json5==0.9.10" ], - "version": "0.1.0", + "version": "0.1.1", "config_flow": true } \ No newline at end of file diff --git a/custom_components/tplink_easy_smart/switch.py b/custom_components/tplink_easy_smart/switch.py index 7c5a5d9..7a7a96b 100644 --- a/custom_components/tplink_easy_smart/switch.py +++ b/custom_components/tplink_easy_smart/switch.py @@ -12,7 +12,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DATA_KEY_COORDINATOR, DOMAIN +from .const import ( + CONF_PORT_STATE_SWITCHES, + DATA_KEY_COORDINATOR, + DEFAULT_PORT_STATE_SWITCHES, + DOMAIN, +) from .helpers import generate_entity_id, generate_entity_name, generate_entity_unique_id from .update_coordinator import TpLinkDataUpdateCoordinator @@ -66,22 +71,25 @@ async def async_setup_entry( sensors = [] - for port_number in range(1, coordinator.ports_count + 1): - sensors.append( - TpLinkPortStateSwitch( - coordinator, - TpLinkPortSwitchEntityDescription( - key=f"port_{port_number}_enabled", - icon="mdi:ethernet", - port_number=port_number, - device_name=coordinator.get_switch_info().name, - function_uid=_FUNCTION_UID_PORT_STATE_FORMAT.format(port_number), - function_name=_FUNCTION_DISPLAYED_NAME_PORT_STATE_FORMAT.format( - port_number + if config_entry.options.get(CONF_PORT_STATE_SWITCHES, DEFAULT_PORT_STATE_SWITCHES): + for port_number in range(1, coordinator.ports_count + 1): + sensors.append( + TpLinkPortStateSwitch( + coordinator, + TpLinkPortSwitchEntityDescription( + key=f"port_{port_number}_enabled", + icon="mdi:ethernet", + port_number=port_number, + device_name=coordinator.get_switch_info().name, + function_uid=_FUNCTION_UID_PORT_STATE_FORMAT.format( + port_number + ), + function_name=_FUNCTION_DISPLAYED_NAME_PORT_STATE_FORMAT.format( + port_number + ), ), - ), + ) ) - ) async_add_entities(sensors) @@ -174,7 +182,6 @@ def __init__( self._attr_extra_state_attributes = {} self._port_number = description.port_number self._attr_icon = "mdi:ethernet" - self._attr_entity_registry_enabled_default = False async def _go_to_state(self, state: bool): info = self._port_info diff --git a/custom_components/tplink_easy_smart/translations/en.json b/custom_components/tplink_easy_smart/translations/en.json index 6ae29a0..26e5d33 100644 --- a/custom_components/tplink_easy_smart/translations/en.json +++ b/custom_components/tplink_easy_smart/translations/en.json @@ -11,19 +11,35 @@ "username": "Username", "password": "Password", "ssl": "Use SSL", - "verify_ssl": "Verify SSL cert", - "scan_interval": "Update interval" + "verify_ssl": "Verify SSL cert" } } }, "error": { "name_exists": "The name already used.", "auth_general": "Can not authenticate.", - "auth_invalid_csrf": "CSRF error, try again.", "auth_invalid_credentials": "Invalid username or password.", "auth_user_blocked": "The user is not allowed to login.", "auth_too_many_users": "The number of the user that allowed to login has been full.", "auth_session_timeout": "The session is timeout." } + }, + "options": { + "step": { + "basic_options": { + "data": { + "scan_interval": "Update interval" + }, + "title": "TP-Link easy smart switch setup (1\/2)", + "description": "Basic options" + }, + "features_select": { + "data": { + "port_state_switches": "Port state switches" + }, + "title": "TP-Link easy smart switch setup (2\/2)", + "description": "Controls" + } + } } } \ No newline at end of file diff --git a/custom_components/tplink_easy_smart/translations/ru.json b/custom_components/tplink_easy_smart/translations/ru.json index d3b6dcb..41a23e8 100644 --- a/custom_components/tplink_easy_smart/translations/ru.json +++ b/custom_components/tplink_easy_smart/translations/ru.json @@ -11,8 +11,7 @@ "username": "Имя пользователя", "password": "Пароль", "ssl": "Использовать SSL", - "verify_ssl": "Проверять сертификат", - "scan_interval": "Период обновления" + "verify_ssl": "Проверять сертификат" } } }, @@ -24,5 +23,23 @@ "auth_too_many_users": "Слишком много активных пользователей.", "auth_session_timeout": "Таймаут сессии истек." } + }, + "options": { + "step": { + "basic_options": { + "data": { + "scan_interval": "Период обновления" + }, + "title": "Настройка интеграции TP-Link Easy Smart (1\/2)", + "description": "Базовые настройки" + }, + "features_select": { + "data": { + "port_state_switches": "Выключатели портов" + }, + "title": "Настройка интеграции TP-Link Easy Smart (2\/2)", + "description": "Элементы управления" + } + } } } \ No newline at end of file diff --git a/custom_components/tplink_easy_smart/update_coordinator.py b/custom_components/tplink_easy_smart/update_coordinator.py index c07cf6d..6a5a629 100644 --- a/custom_components/tplink_easy_smart/update_coordinator.py +++ b/custom_components/tplink_easy_smart/update_coordinator.py @@ -19,7 +19,7 @@ from .client.classes import TpLinkSystemInfo from .client.tplink_api import PortSpeed, PortState, TpLinkApi -from .const import ATTR_MANUFACTURER, DOMAIN +from .const import ATTR_MANUFACTURER, DEFAULT_SCAN_INTERVAL, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -43,12 +43,17 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: self._switch_info: TpLinkSystemInfo | None = None self._port_states: list[PortState] = [] + update_interval = config_entry.options.get( + CONF_SCAN_INTERVAL, + config_entry.data.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), + ) + super().__init__( hass, _LOGGER, name=config_entry.data[CONF_NAME], update_method=self.async_update, - update_interval=timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]), + update_interval=timedelta(seconds=update_interval), ) @property