From 656debd3819e534e14a25b2c4c2a9deec4558625 Mon Sep 17 00:00:00 2001 From: Markus Date: Sat, 11 Nov 2023 00:47:19 +0100 Subject: [PATCH] Add config flow --- custom_components/unifi_voucher/__init__.py | 11 +- custom_components/unifi_voucher/api.py | 219 ++++++++++++++++++ custom_components/unifi_voucher/button.py | 8 +- .../unifi_voucher/config_flow.py | 148 +++++++++--- custom_components/unifi_voucher/const.py | 7 +- custom_components/unifi_voucher/controller.py | 124 ---------- .../unifi_voucher/coordinator.py | 24 +- custom_components/unifi_voucher/entity.py | 4 +- custom_components/unifi_voucher/errors.py | 26 --- custom_components/unifi_voucher/sensor.py | 4 +- custom_components/unifi_voucher/strings.json | 14 +- 11 files changed, 372 insertions(+), 217 deletions(-) create mode 100644 custom_components/unifi_voucher/api.py delete mode 100644 custom_components/unifi_voucher/controller.py delete mode 100644 custom_components/unifi_voucher/errors.py diff --git a/custom_components/unifi_voucher/__init__.py b/custom_components/unifi_voucher/__init__.py index aaa5b52..b752a2d 100644 --- a/custom_components/unifi_voucher/__init__.py +++ b/custom_components/unifi_voucher/__init__.py @@ -2,26 +2,17 @@ from __future__ import annotations from homeassistant.core import HomeAssistant -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, - CONF_VERIFY_SSL, -) from homeassistant.config_entries import ConfigEntry -from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( DOMAIN, PLATFORMS, - CONF_SITE_ID, ) from .coordinator import UnifiVoucherCoordinator async def async_setup(hass: HomeAssistant, config: dict) -> bool: - """Set up DUniFi WiFi Voucher component.""" + """Set up DUniFi Hotspot Manager component.""" hass.data.setdefault(DOMAIN, {}) return True diff --git a/custom_components/unifi_voucher/api.py b/custom_components/unifi_voucher/api.py new file mode 100644 index 0000000..19cb4bf --- /dev/null +++ b/custom_components/unifi_voucher/api.py @@ -0,0 +1,219 @@ +"""UniFi Hotspot Manager API Client.""" +from __future__ import annotations + +import asyncio +import ssl +from dataclasses import dataclass + +from aiohttp import CookieJar +import aiounifi +from aiounifi.interfaces.api_handlers import ItemEvent +from aiounifi.interfaces.sites import Sites +from aiounifi.models.configuration import Configuration +from aiounifi.models.api import ApiItem, ApiRequest + +from homeassistant.core import ( + callback, + HomeAssistant, +) +from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) + +from .const import ( + LOGGER, + DOMAIN, +) + +RETRY_TIMER = 15 + +class UnifiVoucherApiError(Exception): + """Exception to indicate a general API error.""" + + +class UnifiVoucherApiConnectionError(UnifiVoucherApiError): + """Exception to indicate a connection error.""" + + +class UnifiVoucherApiAccessError(UnifiVoucherApiError): + """Exception to indicate an access error.""" + + +class UnifiVoucherApiAuthenticationError(UnifiVoucherApiError): + """Exception to indicate an authentication error.""" + + +@dataclass +class UnifiVoucherListRequest(ApiRequest): + """Request object for device list.""" + + @classmethod + def create( + cls + ) -> Self: + """Create voucher list request.""" + return cls( + method="get", + path="/stat/voucher", + ) + +@dataclass +class UnifiVoucherCreateRequest(ApiRequest): + """Request object for voucher create.""" + + @classmethod + def create( + cls, + number: int, + quota: int, + expire: int, + up_bandwidth: int | None = None, + down_bandwidth: int | None = None, + byte_quota: int | None = None, + note: str | None = None, + ) -> Self: + """ + Create voucher create request. + + :param number: number of vouchers + :param quota: number of using; 0 = unlimited + :param expire: expiration of voucher in minutes + :param up_bandwidth: up speed allowed in kbps + :param down_bandwidth: down speed allowed in kbps + :param byte_quota: quantity of bytes allowed in MB + :param note: description + """ + params = { + "n": number, + "quota": quota, + "expire": "custom", + "expire_number": expire, + "expire_unit": 1, + } + if up_bandwidth: + params["up"] = up_bandwidth + if down_bandwidth: + params["down"] = down_bandwidth + if byte_quota: + params["bytes"] = byte_quota + if note: + params["note"] = note + + return cls( + method="post", + path="/cmd/hotspot", + data={ + "cmd": "create-voucher", + "params": params, + }, + ) + +class UnifiVoucherApiClient: + """API Client.""" + + def __init__( + self, + hass: HomeAssistant, + host: str, + username: str, + password: str, + port: int, + site_id: str, + verify_ssl: bool, + ) -> None: + """Initialize the system.""" + self.hass = hass + self.host = host + + if verify_ssl: + session = aiohttp_client.async_get_clientsession( + hass, + ) + else: + session = aiohttp_client.async_create_clientsession( + hass, + verify_ssl=False, + cookie_jar=CookieJar(unsafe=True), + ) + self.api = aiounifi.Controller( + Configuration( + session, + host=host, + username=username, + password=password, + port=port, + site=site_id, + ssl_context=verify_ssl, + ) + ) + self.available = True + + @callback + def reconnect(self) -> None: + """Prepare to reconnect UniFi session.""" + LOGGER.info("Will try to reconnect to UniFi Network") + self.hass.loop.create_task(self.async_reconnect()) + + async def async_reconnect(self) -> None: + """Try to reconnect UniFi Network session.""" + try: + async with asyncio.timeout(5): + await self.api.login() + except ( + asyncio.TimeoutError, + aiounifi.BadGateway, + aiounifi.ServiceUnavailable, + aiounifi.AiounifiException, + ): + self.hass.loop.call_later(RETRY_TIMER, self.reconnect) + + async def check_api_user( + self, + ) -> dict[str, any]: + """Check the given API user.""" + _sites = {} + try: + async with asyncio.timeout(10): + await self.api.login() + + await self.api.sites.update() + for _id, _site in self.api.sites.items(): + if _site.role in ("admin", "hotspot"): + _sites[_id] = _site.description + if len(_sites) == 0: + LOGGER.warning( + "Connected to UniFi Network at %s but no access.", + self.host, + ) + raise UnifiVoucherApiAccessError + return _sites + except ( + asyncio.TimeoutError, + aiounifi.BadGateway, + aiounifi.ServiceUnavailable, + aiounifi.RequestError, + aiounifi.ResponseError, + ) as err: + LOGGER.error( + "Error connecting to the UniFi Network at %s: %s", + self.host, + err, + ) + raise UnifiVoucherApiConnectionError from err + except ( + aiounifi.LoginRequired, + aiounifi.Unauthorized, + aiounifi.Forbidden, + ) as err: + LOGGER.warning( + "Connected to UniFi Network at %s but login required: %s", + self.host, + err, + ) + raise UnifiVoucherApiAuthenticationError from err + except aiounifi.AiounifiException as err: + LOGGER.exception("Unknown UniFi Network communication error occurred: %s", err) + raise UnifiVoucherApiError from err + return False diff --git a/custom_components/unifi_voucher/button.py b/custom_components/unifi_voucher/button.py index 04c6d53..328b3bb 100644 --- a/custom_components/unifi_voucher/button.py +++ b/custom_components/unifi_voucher/button.py @@ -1,4 +1,4 @@ -"""UniFi WiFi Voucher button platform.""" +"""UniFi Hotspot Manager button platform.""" from __future__ import annotations from collections.abc import Callable, Coroutine @@ -25,7 +25,7 @@ @dataclass class UnifiVoucherButtonDescriptionMixin: - """Mixin to describe a UniFi WiFi Voucher button entity.""" + """Mixin to describe a UniFi Hotspot Manager button entity.""" press_action: Callable[[UnifiVoucherCoordinator], Coroutine] @@ -35,7 +35,7 @@ class UnifiVoucherButtonDescription( ButtonEntityDescription, UnifiVoucherButtonDescriptionMixin, ): - """UniFi WiFi Voucher button description.""" + """UniFi Hotspot Manager button description.""" async def async_setup_entry( @@ -69,7 +69,7 @@ async def async_setup_entry( class UnifiVoucherButton(UnifiVoucherEntity, ButtonEntity): - """Representation of a UniFi WiFi Voucher button.""" + """Representation of a UniFi Hotspot Manager button.""" def __init__( self, diff --git a/custom_components/unifi_voucher/config_flow.py b/custom_components/unifi_voucher/config_flow.py index 1458537..bfcc24e 100644 --- a/custom_components/unifi_voucher/config_flow.py +++ b/custom_components/unifi_voucher/config_flow.py @@ -1,9 +1,15 @@ -"""Adds config flow for UniFi WiFi Voucher.""" +"""Adds config flow for UniFi Hotspot Manager.""" from __future__ import annotations -from types import MappingProxyType +from aiounifi.interfaces.sites import Sites +from aiounifi.models.site import Site +from homeassistant.core import ( + callback, + HomeAssistant, +) from homeassistant.config_entries import ( + ConfigEntry, ConfigFlow, CONN_CLASS_LOCAL_PUSH, ) @@ -18,9 +24,6 @@ from homeassistant.helpers import ( selector, ) -from homeassistant.helpers.aiohttp_client import ( - async_create_clientsession, -) import voluptuous as vol import socket @@ -29,22 +32,32 @@ DOMAIN, DEFAULT_SITE_ID, DEFAULT_HOST, + DEFAULT_USERNAME, + DEFAULT_PASSWORD, DEFAULT_PORT, DEFAULT_VERIFY_SSL, CONF_SITE_ID, ) -from .controller import get_unifi_controller -from .errors import AuthenticationRequired, CannotConnect +from .api import ( + UnifiVoucherApiClient, + UnifiVoucherApiAuthenticationError, + UnifiVoucherApiAccessError, + UnifiVoucherApiConnectionError, + UnifiVoucherApiError, +) class UnifiVoucherConfigFlow(ConfigFlow, domain=DOMAIN): - """UniFi WiFi Voucher config flow.""" + """UniFi Hotspot Manager config flow.""" VERSION = 1 CONNECTION_CLASS = CONN_CLASS_LOCAL_PUSH - data: dict[str, any] | None - options: dict[str, any] | None + def __init__(self) -> None: + """Initialize the UniFi Network flow.""" + self._title: str | None = None + self._options: dict[str, any] | None = None + self._sites: dict[str, str] | None = None async def async_step_user( self, @@ -56,40 +69,55 @@ async def async_step_user( self._async_abort_entries_match( { CONF_HOST: user_input[CONF_HOST], - CONF_PORT: user_input[CONF_PORT], + CONF_USERNAME: user_input[CONF_USERNAME], } ) - _data = { - CONF_HOST: user_input[CONF_HOST], - CONF_USERNAME: user_input[CONF_USERNAME], - CONF_PASSWORD: user_input[CONF_PASSWORD], - CONF_PORT: int(user_input[CONF_PORT]), - CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL], - CONF_SITE_ID: DEFAULT_SITE_ID, - } try: - session = async_create_clientsession(self.hass, user_input[CONF_VERIFY_SSL]) - controller = await get_unifi_controller( - self.hass, MappingProxyType(_data) + client = UnifiVoucherApiClient( + self.hass, + host=user_input[CONF_HOST], + username=user_input[CONF_USERNAME], + password=user_input[CONF_PASSWORD], + port=int(user_input[CONF_PORT]), + site_id=DEFAULT_SITE_ID, + verify_ssl=user_input[CONF_VERIFY_SSL], ) - await controller.sites.update() - self.sites = controller.sites - LOGGER.debug(controller.sites) - except AuthenticationRequired: - errors["base"] = "invalid_auth" - except CannotConnect: + self._sites = await client.check_api_user() + except UnifiVoucherApiConnectionError: errors["base"] = "cannot_connect" - except Exception as exception: + except UnifiVoucherApiAuthenticationError: + errors["base"] = "invalid_auth" + except UnifiVoucherApiAccessError: + errors["base"] = "no_access" + except (UnifiVoucherApiError, Exception) as exception: LOGGER.exception(exception) errors["base"] = "unknown" if not errors: # Input is valid, set data - self.data = _data - self.data.update() + self._options = { + CONF_HOST: user_input.get(CONF_HOST, "").strip(), + CONF_USERNAME: user_input.get(CONF_USERNAME, "").strip(), + CONF_PASSWORD: user_input.get(CONF_PASSWORD, "").strip(), + CONF_PORT: int(user_input[CONF_PORT]), + CONF_SITE_ID: DEFAULT_SITE_ID, + CONF_VERIFY_SSL: user_input.get(CONF_VERIFY_SSL, False), + } + # Go to site selection, if user has access to more than one site + if len(self._sites) > 1: + return await self.async_step_site() + + site_id = list(self._sites.keys())[0] + + self._title = self._sites.get(site_id) + self._options.update({ + CONF_SITE_ID: site_id + }) + # User is done, create the config entry. return self.async_create_entry( - title=self.data[CONF_HOST], - data=self.data, + title=self._title, + data={}, + options=self._options, ) if await _async_discover_unifi( @@ -111,7 +139,7 @@ async def async_step_user( ), vol.Required( CONF_USERNAME, - default=(user_input or {}).get(CONF_USERNAME, ""), + default=(user_input or {}).get(CONF_USERNAME, DEFAULT_USERNAME), ): selector.TextSelector( selector.TextSelectorConfig( type=selector.TextSelectorType.TEXT @@ -119,7 +147,7 @@ async def async_step_user( ), vol.Required( CONF_PASSWORD, - default=(user_input or {}).get(CONF_PASSWORD, ""), + default=(user_input or {}).get(CONF_PASSWORD, DEFAULT_PASSWORD), ): selector.TextSelector( selector.TextSelectorConfig( type=selector.TextSelectorType.TEXT @@ -144,6 +172,56 @@ async def async_step_user( errors=errors, ) + async def async_step_site( + self, + user_input: dict[str, any] | None = None, + ) -> FlowResult: + """Second step in config flow to save site.""" + errors: dict[str, str] = {} + if user_input is not None: + site_id = user_input.get(CONF_SITE_ID, "") + + if not self._sites.get(site_id): + errors["base"] = "site_invalid" + + if not errors: + # Input is valid, set data. + self._title = self._sites.get(site_id) + self._options.update({ + CONF_SITE_ID: site_id + }) + # User is done, create the config entry. + return self.async_create_entry( + title=self._title, + data={}, + options=self._options, + ) + return self.async_show_form( + step_id="site", + data_schema=vol.Schema( + { + vol.Required( + CONF_SITE_ID, + default=(user_input or {}).get(CONF_SITE_ID, ""), + ): selector.SelectSelector( + selector.SelectSelectorConfig( + options=[ + selector.SelectOptionDict( + value=site_id, + label=site_description, + ) + for site_id, site_description in self._sites.items() + ], + mode=selector.SelectSelectorMode.DROPDOWN, + translation_key=CONF_SITE_ID, + multiple=False, + ) + ), + } + ), + errors=errors, + ) + async def _async_discover_unifi(hass: HomeAssistant) -> str | None: """Discover UniFi Network address.""" try: diff --git a/custom_components/unifi_voucher/const.py b/custom_components/unifi_voucher/const.py index a953f29..f2d3963 100644 --- a/custom_components/unifi_voucher/const.py +++ b/custom_components/unifi_voucher/const.py @@ -1,12 +1,9 @@ """Constants for UniFi Hotspot Manager integration.""" from logging import getLogger -import voluptuous as vol from homeassistant.const import ( Platform, - CONF_DEVICE_ID, ) -from homeassistant.helpers import config_validation as cv LOGGER = getLogger(__package__) @@ -19,15 +16,15 @@ ] DEFAULT_SITE_ID = "default" -DEFAULT_VERSION = "UDMP-unifiOS" DEFAULT_HOST = "" +DEFAULT_USERNAME = "" +DEFAULT_PASSWORD = "" DEFAULT_PORT = 443 DEFAULT_VERIFY_SSL = False UPDATE_INTERVAL = 120 CONF_SITE_ID = "site" -CONF_VERSION = "version" ATTR_EXTRA_STATE_ATTRIBUTES = "extra_state_attributes" ATTR_AVAILABLE = "available" diff --git a/custom_components/unifi_voucher/controller.py b/custom_components/unifi_voucher/controller.py deleted file mode 100644 index dac2c42..0000000 --- a/custom_components/unifi_voucher/controller.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Controller for UniFi WiFi Voucher.""" -from __future__ import annotations - -import asyncio -from datetime import datetime, timedelta -import ssl -from types import MappingProxyType -from typing import Any - -from aiohttp import CookieJar -import aiounifi -from aiounifi.interfaces.api_handlers import ItemEvent -from aiounifi.models.configuration import Configuration -from aiounifi.models.device import DeviceSetPoePortModeRequest - -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, - CONF_VERIFY_SSL, - Platform, -) -from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback -from homeassistant.helpers import ( - aiohttp_client, - device_registry as dr, - entity_registry as er, -) -from homeassistant.helpers.device_registry import ( - DeviceEntry, - DeviceEntryType, - DeviceInfo, -) -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, - async_dispatcher_send, -) -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_entries_for_config_entry -from homeassistant.helpers.event import async_call_later, async_track_time_interval -import homeassistant.util.dt as dt_util - -from .const import ( - DOMAIN, - LOGGER, - UPDATE_INTERVAL, - DEFAULT_SITE_ID, - DEFAULT_VERSION, - DEFAULT_HOST, - DEFAULT_PORT, - DEFAULT_VERIFY_SSL, - CONF_SITE_ID, - CONF_VERSION, - ATTR_AVAILABLE, - ATTR_LAST_PULL, -) -from .errors import AuthenticationRequired, CannotConnect - -async def get_unifi_controller( - hass: HomeAssistant, - config: MappingProxyType[str, Any], -) -> aiounifi.Controller: - """Create a controller object and verify authentication.""" - ssl_context: ssl.SSLContext | bool = False - - if verify_ssl := config.get(CONF_VERIFY_SSL): - session = aiohttp_client.async_get_clientsession(hass) - if isinstance(verify_ssl, str): - ssl_context = ssl.create_default_context(cafile=verify_ssl) - else: - session = aiohttp_client.async_create_clientsession( - hass, verify_ssl=False, cookie_jar=CookieJar(unsafe=True) - ) - - controller = aiounifi.Controller( - Configuration( - session, - host=config[CONF_HOST], - username=config[CONF_USERNAME], - password=config[CONF_PASSWORD], - port=config[CONF_PORT], - site=config[CONF_SITE_ID], - ssl_context=ssl_context, - ) - ) - - try: - async with asyncio.timeout(10): - await controller.login() - return controller - - except aiounifi.Unauthorized as err: - LOGGER.warning( - "Connected to UniFi Network at %s but not registered: %s", - config[CONF_HOST], - err, - ) - raise AuthenticationRequired from err - - except ( - asyncio.TimeoutError, - aiounifi.BadGateway, - aiounifi.ServiceUnavailable, - aiounifi.RequestError, - aiounifi.ResponseError, - ) as err: - LOGGER.error( - "Error connecting to the UniFi Network at %s: %s", config[CONF_HOST], err - ) - raise CannotConnect from err - - except aiounifi.LoginRequired as err: - LOGGER.warning( - "Connected to UniFi Network at %s but login required: %s", - config[CONF_HOST], - err, - ) - raise AuthenticationRequired from err - - except aiounifi.AiounifiException as err: - LOGGER.exception("Unknown UniFi Network communication error occurred: %s", err) - raise AuthenticationRequired from err diff --git a/custom_components/unifi_voucher/coordinator.py b/custom_components/unifi_voucher/coordinator.py index 4c71072..aa1c687 100644 --- a/custom_components/unifi_voucher/coordinator.py +++ b/custom_components/unifi_voucher/coordinator.py @@ -1,4 +1,4 @@ -"""DataUpdateCoordinator for UniFi WiFi Voucher.""" +"""DataUpdateCoordinator for UniFi Hotspot Manager.""" from __future__ import annotations from datetime import timedelta @@ -23,21 +23,25 @@ DOMAIN, LOGGER, UPDATE_INTERVAL, - DEFAULT_SITE_ID, - DEFAULT_VERSION, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_VERIFY_SSL, + DEFAULT_SITE_ID, CONF_SITE_ID, - CONF_VERSION, ATTR_AVAILABLE, ATTR_LAST_PULL, ) +from .api import ( + UnifiVoucherApiClient, + UnifiVoucherApiAuthenticationError, + UnifiVoucherApiConnectionError, + UnifiVoucherApiError, +) # https://developers.home-assistant.io/docs/integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities class UnifiVoucherCoordinator(DataUpdateCoordinator): - """Class to manage fetching data from the UniFi WiFi Voucher.""" + """Class to manage fetching data from the UniFi Hotspot Manager.""" def __init__( self, @@ -53,7 +57,15 @@ def __init__( update_interval=update_interval, ) self.config_entry = config_entry - self.controller = None # TODO + self.client = UnifiVoucherApiClient( + hass, + host=config_entry.options.get(CONF_HOST, DEFAULT_HOST), + username=config_entry.options.get(CONF_USERNAME, ""), + password=config_entry.options.get(CONF_PASSWORD, ""), + port=int(config_entry.options.get(CONF_PORT, DEFAULT_PORT)), + site_id=config_entry.options.get(CONF_SITE_ID, DEFAULT_SITE_ID), + verify_ssl=config_entry.options.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL), + ) self._last_pull = None async def __aenter__(self): diff --git a/custom_components/unifi_voucher/entity.py b/custom_components/unifi_voucher/entity.py index 272c57d..2aaf41f 100644 --- a/custom_components/unifi_voucher/entity.py +++ b/custom_components/unifi_voucher/entity.py @@ -1,4 +1,4 @@ -"""UniFi WiFi Voucher entity.""" +"""UniFi Hotspot Manager entity.""" from __future__ import annotations from homeassistant.core import callback @@ -23,7 +23,7 @@ class UnifiVoucherEntity(CoordinatorEntity): - """UniFi WiFi Voucher class.""" + """UniFi Hotspot Manager class.""" _attr_has_entity_name = True diff --git a/custom_components/unifi_voucher/errors.py b/custom_components/unifi_voucher/errors.py deleted file mode 100644 index c3b2bb2..0000000 --- a/custom_components/unifi_voucher/errors.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Errors for the UniFi Network integration.""" -from homeassistant.exceptions import HomeAssistantError - - -class UnifiException(HomeAssistantError): - """Base class for UniFi Network exceptions.""" - - -class AlreadyConfigured(UnifiException): - """Controller is already configured.""" - - -class AuthenticationRequired(UnifiException): - """Unknown error occurred.""" - - -class CannotConnect(UnifiException): - """Unable to connect to the controller.""" - - -class LoginRequired(UnifiException): - """Integration got logged out.""" - - -class UserLevel(UnifiException): - """User level too low.""" diff --git a/custom_components/unifi_voucher/sensor.py b/custom_components/unifi_voucher/sensor.py index bd1e16b..07ef789 100644 --- a/custom_components/unifi_voucher/sensor.py +++ b/custom_components/unifi_voucher/sensor.py @@ -1,4 +1,4 @@ -"""UniFi WiFi Voucher sensor platform.""" +"""UniFi Hotspot Manager sensor platform.""" from __future__ import annotations from homeassistant.core import HomeAssistant @@ -57,7 +57,7 @@ async def async_setup_entry( class UnifiVoucherSensor(UnifiVoucherEntity, SensorEntity): - """Representation of a UniFi WiFi Voucher sensor.""" + """Representation of a UniFi Hotspot Manager sensor.""" def __init__( self, diff --git a/custom_components/unifi_voucher/strings.json b/custom_components/unifi_voucher/strings.json index dd499ce..29bce85 100644 --- a/custom_components/unifi_voucher/strings.json +++ b/custom_components/unifi_voucher/strings.json @@ -2,23 +2,31 @@ "config": { "step": { "user": { - "description": "Set up your UniFi WiFi Voucher Manager instance.", + "description": "Set up your UniFi Hotspot Manager Manager instance.", "data": { "host": "[%key:common::config_flow::data::host%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", "port": "[%key:common::config_flow::data::port%]", - "ssl": "[%key:common::config_flow::data::ssl%]", "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" } + }, + "site": { + "title": "Select site", + "description": "Select the site for which you want to manage the vouchers.", + "data": { + "site_id": "Select site" + } } }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "invalid_host": "[%key:common::config_flow::error::invalid_host%]", + "no_access": "No access", "unknown": "[%key:common::config_flow::error::unknown%]", - "timeout_connect": "[%key:common::config_flow::error::timeout_connect%]" + "timeout_connect": "[%key:common::config_flow::error::timeout_connect%]", + "site_invalid": "Site invalid" }, "abort": { "existing_instance_updated": "Updated existing configuration",