Skip to content

Commit

Permalink
Add explicit source selection for entity filter (dext0r#477)
Browse files Browse the repository at this point in the history
  • Loading branch information
dext0r committed Jan 9, 2024
1 parent 7af58a1 commit 715c693
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 170 deletions.
27 changes: 19 additions & 8 deletions custom_components/yandex_smart_home/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
from homeassistant.const import SERVICE_RELOAD
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entityfilter import BASE_FILTER_SCHEMA, FILTER_SCHEMA
from homeassistant.helpers.entityfilter import BASE_FILTER_SCHEMA, FILTER_SCHEMA, EntityFilter
from homeassistant.helpers.reload import async_integration_yaml_config
import voluptuous as vol

from . import config_validation as ycv, const
from .cloud import delete_cloud_instance
from .const import DOMAIN, ConnectionType
from .const import DOMAIN, ConnectionType, EntityFilterSource
from .entry_data import ConfigEntryData
from .http import async_register_http

Expand Down Expand Up @@ -206,15 +206,16 @@ def get_diagnostics(self) -> ConfigType:

return {"yaml_config": async_redact_data(self._yaml_config, [const.CONF_NOTIFIER])}

def yaml_config_has_filter(self) -> bool:
"""Test if yaml configuration has filters defined."""
return const.CONF_FILTER in self._yaml_config

async def async_setup_entry(self, entry: ConfigEntry) -> bool:
"""Set up a config entry."""
entity_config = self._yaml_config.get(const.CONF_ENTITY_CONFIG)
entity_filter_config = self._yaml_config.get(const.CONF_FILTER, entry.options.get(const.CONF_FILTER))
entity_filter = FILTER_SCHEMA(entity_filter_config) if entity_filter_config else None

entity_filter: EntityFilter | None = None
if entry.options.get(const.CONF_FILTER_SOURCE) == EntityFilterSource.YAML:
if entity_filter_config := self._yaml_config.get(const.CONF_FILTER):
entity_filter = FILTER_SCHEMA(entity_filter_config)
else:
entity_filter = FILTER_SCHEMA(entry.options.get(const.CONF_FILTER, {}))

data = ConfigEntryData(
hass=self._hass,
Expand Down Expand Up @@ -267,6 +268,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate the config entry upon new versions."""
version = entry.version
component: YandexSmartHome = hass.data[DOMAIN]
data = {**entry.data}
options = {**entry.options}

Expand Down Expand Up @@ -301,6 +303,15 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
version = entry.version = 3
_LOGGER.debug(f"Migration to version {version} successful")

if version == 3:
options[const.CONF_FILTER_SOURCE] = EntityFilterSource.CONFIG_ENTRY
if const.CONF_FILTER in component._yaml_config:
options[const.CONF_FILTER_SOURCE] = EntityFilterSource.YAML

version = entry.version = 4
hass.config_entries.async_update_entry(entry, data=data, options=options)
_LOGGER.debug(f"Migration to version {version} successful")

return True


Expand Down
77 changes: 55 additions & 22 deletions custom_components/yandex_smart_home/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,38 @@
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import selector
from homeassistant.helpers.entityfilter import CONF_INCLUDE_ENTITIES
from homeassistant.helpers.selector import SelectOptionDict, SelectSelector, SelectSelectorConfig, SelectSelectorMode
import voluptuous as vol

from . import DOMAIN, FILTER_SCHEMA, const
from .cloud import register_cloud_instance
from .const import ConnectionType
from .const import ConnectionType, EntityFilterSource

if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import ConfigType

from . import YandexSmartHome

_LOGGER = logging.getLogger(__name__)

CONNECTION_TYPES = {ConnectionType.CLOUD: "Через облако", ConnectionType.DIRECT: "Напрямую"}
DEFAULT_CONFIG_ENTRY_TITLE = "Yandex Smart Home"
FILTER_SOURCE_SELECTOR = SelectSelector(
SelectSelectorConfig(
mode=SelectSelectorMode.LIST,
translation_key=const.CONF_FILTER_SOURCE,
options=[
SelectOptionDict(value=EntityFilterSource.CONFIG_ENTRY, label=EntityFilterSource.CONFIG_ENTRY),
SelectOptionDict(value=EntityFilterSource.YAML, label=EntityFilterSource.YAML),
],
),
)


class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Yandex Smart Home."""

VERSION = 3
VERSION = 4

def __init__(self) -> None:
"""Initialize a config flow handler."""
Expand All @@ -48,19 +57,32 @@ async def async_step_user(self, user_input: ConfigType | None = None) -> FlowRes
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")

if DOMAIN in self.hass.data:
component: YandexSmartHome = self.hass.data[DOMAIN]
if component.yaml_config_has_filter():
return await self.async_step_filter_yaml()
if user_input is not None:
return await self.async_step_expose_settings()

return await self.async_step_include_entities()
return self.async_show_form(step_id="user")

async def async_step_filter_yaml(self, user_input: ConfigType | None = None) -> FlowResult:
"""Show warning about filter was configured in yaml."""
async def async_step_expose_settings(self, user_input: ConfigType | None = None) -> FlowResult:
"""Choose entity expose settings."""
if user_input is not None:
return await self.async_step_connection_type()
self._options.update(user_input)

return self.async_show_form(step_id="filter_yaml")
match user_input[const.CONF_FILTER_SOURCE]:
case EntityFilterSource.CONFIG_ENTRY:
return await self.async_step_include_entities()
case EntityFilterSource.YAML:
return await self.async_step_connection_type()

return self.async_show_form(
step_id="expose_settings",
data_schema=vol.Schema(
{
vol.Optional(
const.CONF_FILTER_SOURCE, default=EntityFilterSource.CONFIG_ENTRY
): FILTER_SOURCE_SELECTOR
}
),
)

async def async_step_include_entities(self, user_input: ConfigType | None = None) -> FlowResult:
"""Choose entities that should be exposed."""
Expand Down Expand Up @@ -141,27 +163,38 @@ def __init__(self, entry: ConfigEntry):

async def async_step_init(self, _: ConfigType | None = None) -> FlowResult:
"""Show menu."""
options = ["include_entities", "connection_type"]
options = ["expose_settings", "connection_type"]
if self._data[const.CONF_CONNECTION_TYPE] == ConnectionType.CLOUD:
options += ["cloud_info", "cloud_settings"]

return self.async_show_menu(step_id="init", menu_options=options)

async def async_step_filter_yaml(self, user_input: ConfigType | None = None) -> FlowResult:
"""Show warning about filter was configured in yaml."""
async def async_step_expose_settings(self, user_input: ConfigType | None = None) -> FlowResult:
"""Choose entity expose settings."""
if user_input is not None:
return await self.async_step_init()
self._options.update(user_input)

match user_input[const.CONF_FILTER_SOURCE]:
case EntityFilterSource.CONFIG_ENTRY:
return await self.async_step_include_entities()

return self.async_show_form(step_id="filter_yaml")
return await self.async_step_done()

return self.async_show_form(
step_id="expose_settings",
data_schema=vol.Schema(
{
vol.Optional(
const.CONF_FILTER_SOURCE, default=self._options[const.CONF_FILTER_SOURCE]
): FILTER_SOURCE_SELECTOR
}
),
)

async def async_step_include_entities(self, user_input: ConfigType | None = None) -> FlowResult:
"""Choose entities that should be exposed."""

errors = {}
entities: set[str] = set()
component: YandexSmartHome = self.hass.data[DOMAIN]
if component.yaml_config_has_filter():
return await self.async_step_filter_yaml()

if const.CONF_FILTER in self._options:
entities = set(self._options[const.CONF_FILTER].get(CONF_INCLUDE_ENTITIES, []))
Expand Down
8 changes: 8 additions & 0 deletions custom_components/yandex_smart_home/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
CONF_COLOR_PROFILE = "color_profile"
CONF_ENTITY_CONFIG = "entity_config"
CONF_FILTER = "filter"
CONF_FILTER_SOURCE = "filter_source"
CONF_NAME = "name"
CONF_ROOM = "room"
CONF_TYPE = "type"
Expand Down Expand Up @@ -165,6 +166,13 @@ class ConnectionType(StrEnum):
CLOUD = "cloud"


class EntityFilterSource(StrEnum):
"""Possible sources for entity filter."""

CONFIG_ENTRY = "config_entry"
YAML = "yaml"


class MediaPlayerFeature(StrEnum):
"""Media player feature that user can force enable."""

Expand Down
29 changes: 22 additions & 7 deletions custom_components/yandex_smart_home/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@
"cloud": "**Облачное подключение настроено!**\n\nРеквизиты для привязки Home Assistant к УДЯ:\n* ID: `{id}`\n* Пароль: `{password}`\n\nРеквизиты продублированы в настройках интеграции, сохранять их не требуется.\n\nТеперь вы можете добавить Home Assistant в Умный дом Яндекса, для этого:\n * Откройте [квазар](https://yandex.ru/quasar/iot) или приложение [Дом с Алисой](https://mobile.yandex.ru/apps/smarthome)\n* Нажмите кнопку \"+\" в правом верхнем углу, выберите \"Устройство умного дома\"\n* Найдите в списке и выберите производителя \"Yaha Cloud\"\n* Нажмите кнопку \"Привязать к Яндексу\", откроется страница авторизации\n* Выполните привязку используя реквизиты выше\n\nВ [настройках интеграции](https://docs.yaha-cloud.ru/master/config/getting-started/#gui) вы можете выбрать пользователя, который будет отображаться в Журнале событий."
},
"step": {
"filter_yaml": {
"user" : {
"description": "Добро пожаловать в мастер добавления интеграции Yandex Smart Home!\n\nПосле добавления интеграции вы можете изменить любые её параметры в [настройках](https://docs.yaha-cloud.ru/master/config/getting-started/#gui), удалять интеграцию и настраивать заново не требуется.\n\nПолезные ссылки:\n* Документация: https://docs.yaha-cloud.ru/master\n* Чат в Телеграм: https://t.me/yandex_smart_home"
},
"expose_settings": {
"title": "Объекты для передачи в УДЯ",
"description": "Объекты для передачи в УДЯ выбраны через YAML конфигурацию. Для настройки через интерфейс вы можете удалить (или закоментировать) раздел `filter`.\n\nНажмите \"Подтвердить\" для продолжения настройки интеграции"
"data": {
"filter_source": "Способ выбора объектов для передачи в УДЯ"
}
},
"include_entities": {
"title": "Объекты для передачи в УДЯ",
"description": "Выберите объекты, которые будут переданы в УДЯ.\n\nПосле исключения объекта из этого списка необходимо **вручную удалить** устройство из УДЯ, в противном случае оно будет работать некорректно. Удаление всех устройств возможно через [отвязку навыка](https://docs.yaha-cloud.ru/master/quasar/#unlink)",
"description": "Выберите объекты, которые будут переданы в УДЯ в виде устройств.\n\nПри начальной настройке рекомендуется сократить этот список до минимума.",
"data": {
"entities": "Объекты"
}
Expand All @@ -39,19 +44,21 @@
"step": {
"init": {
"menu_options": {
"include_entities": "Объекты для передачи в УДЯ",
"expose_settings": "Объекты для передачи в УДЯ",
"connection_type": "Тип подключения",
"cloud_info": "ID и пароль (облачное подключение)",
"cloud_settings": "Настройки"
}
},
"filter_yaml": {
"expose_settings": {
"title": "Объекты для передачи в УДЯ",
"description": "Объекты для передачи в УДЯ выбраны через YAML конфигурацию, для настройки через интерфейс удалите (или закоментируйте) раздел `filter`"
"data": {
"filter_source": "Способ выбора объектов для передачи в УДЯ"
}
},
"include_entities": {
"title": "Объекты для передачи в УДЯ",
"description": "Выберите объекты, которые будут переданы в УДЯ.\n\nПосле исключения объекта из этого списка необходимо **вручную удалить** устройство из УДЯ, в противном случае оно будет работать некорректно. Удаление всех устройств возможно через [отвязку навыка](https://docs.yaha-cloud.ru/master/quasar/#unlink)",
"description": "Выберите объекты, которые будут переданы в УДЯ в виде устройств.\n\nЕсли устройство присутствует в УДЯ – соответствующий объект всегда должен находиться в этом списке.\n\nУдаление устройств из УДЯ возможно **только** вручную, для удаления всех устройств [отвяжите навык](https://docs.yaha-cloud.ru/master/quasar/#unlink).",
"data": {
"entities": "Объекты"
}
Expand Down Expand Up @@ -81,5 +88,13 @@
"description": "Параметр `pressure_unit` (раздел `settings`) больше не поддерживается, удалите его из YAML конфигурации.\n\nТеперь компонент автоматически пытается сохранить единицы измерения при передаче значений датчиков из Home Assistant в УДЯ ([подробнее о конвертации значений](https://docs.yaha-cloud.ru/master/devices/sensor/float/#unit-conversion))",
"title": "Устаревший параметр pressure_unit"
}
},
"selector": {
"filter_source": {
"options": {
"config_entry": "Через интерфейс (рекомендуется)",
"yaml": "Через YAML конфигурацию"
}
}
}
}
10 changes: 6 additions & 4 deletions docs/config/filter.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
По умолчанию в УДЯ не передаются никакие объекты.
Выбрать объекты, которые будут переданы в УДЯ можно двумя способами (в зависимости от ваших предпочтений).

Выбрать объекты, которые будут переданы в УДЯ в виде устройств можно двумя способами (в зависимости от ваших предпочтений): YAML конфигурация или интерфейс Home Assistant.

!!! danger "Внимание!"
Устройства из УДЯ удаляются **только вручную**. Удалите устройство из УДЯ после исключения объекта из списка для передачи.
Удаление устройств из УДЯ возможно **только** вручную. Удаляйте устройство из УДЯ только после исключения объекта из списка для передачи.
Для удаления всех устройств - [отвяжите навык (производителя)](../quasar.md#unlink).

!!! warning "Недопустимо оставлять в УДЯ устройства, у которых объект не выбран для передачи. Такие устройства будут работать некорректно."

## Через интерфейс { id=gui }
Выбрать объекты для передачи можно в [настройках интеграции](../config/getting-started.md#gui).
Этот способ будет недоступен, если устройства выбраны в YAML конфигурации.
Выбрать объекты для передачи можно в [настройках интеграции](../config/getting-started.md#gui) --> Объекты для передачи в УДЯ:

![](../assets/images/filter-gui.png){ width=750 }

Expand All @@ -25,6 +25,8 @@

Приоритизация по фильтрам работает аналогично интеграции [Recorder](https://www.home-assistant.io/integrations/recorder/#configure-filter).

!!! warning "Выбор объектов через YAML конфигурацию должен быть явно включен в [настройках интеграции](../config/getting-started.md#gui) --> Объекты для передачи в УДЯ."

!!! example "Пример"
```yaml
yandex_smart_home:
Expand Down
3 changes: 1 addition & 2 deletions docs/config/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
Интеграция поддерживает настройку двумя способами: YAML конфигурация и интерфейс Home Assistant.
Интеграция настраивается двумя способами: YAML конфигурация и интерфейс Home Assistant.
Оба способа могут быть задействованны **одновременно**.
Если какой либо параметр настроен в YAML - его настройка через интерфейс будет невозможна.

## Через интерфейс Home Assistant { id=gui }
На странице `Настройки` --> `Устройства и службы` --> [`Интеграции`](https://my.home-assistant.io/redirect/integrations/) найдите
Expand Down
Loading

0 comments on commit 715c693

Please sign in to comment.