v2.5.3 — поддержка новых устройств: Yeelight, ёлка, гирлянда, ТВ; фильтр sub-entities switch; мониторинг сцен
This commit is contained in:
+83
-28
@@ -2,7 +2,7 @@
|
|||||||
title: Home Assistant Smart Control
|
title: Home Assistant Smart Control
|
||||||
description: Управление умным домом через Home Assistant REST API с интерпретацией естественного языка.
|
description: Управление умным домом через Home Assistant REST API с интерпретацией естественного языка.
|
||||||
author: ChatGPT (refactored by MiniMax-M3)
|
author: ChatGPT (refactored by MiniMax-M3)
|
||||||
version: 2.5.2
|
version: 2.5.3
|
||||||
license: MIT
|
license: MIT
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ class Tools:
|
|||||||
request_timeout: int = Field(default=10, description="Таймаут HTTP-запроса, сек")
|
request_timeout: int = Field(default=10, description="Таймаут HTTP-запроса, сек")
|
||||||
max_retries: int = Field(default=2, description="Количество ретраев при сетевых сбоях")
|
max_retries: int = Field(default=2, description="Количество ретраев при сетевых сбоях")
|
||||||
cache_ttl: int = Field(default=5, description="TTL кэша состояний HA, сек")
|
cache_ttl: int = Field(default=5, description="TTL кэша состояний HA, сек")
|
||||||
confirm_global_actions: bool = Field(default=True, description="Спрашивать подтверждение для команд на ВСЕ сущности домена")
|
confirm_global_actions: bool = Field(default=True, description="Спрашивать подтверждение для глобальных команд")
|
||||||
dry_run: bool = Field(default=False, description="Не выполнять команды, только показывать что БУДЕТ сделано")
|
dry_run: bool = Field(default=False, description="Не выполнять команды, только показывать")
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.valves = self.Valves()
|
self.valves = self.Valves()
|
||||||
@@ -45,11 +45,24 @@ class Tools:
|
|||||||
"устройство", "все", "весь", "вся",
|
"устройство", "все", "весь", "вся",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Алиасы людей — препятствуют "глобальной" команде света
|
||||||
|
_PEOPLE_NAMES = {"дима", "диан", "елен", "анатоли", "милен", "admin"}
|
||||||
|
|
||||||
|
# Sub-entities switch, которые НЕ являются реальными розетками/выключателями.
|
||||||
|
# Обычно создаются Zigbee2MQTT или прошивкой устройств.
|
||||||
|
_SWITCH_SUB_ENTITIES = (
|
||||||
|
"power_outage_memory", "interlock", "do_not_disturb", "child_lock",
|
||||||
|
"permit_join", "curtain_motor_reverse", "reverse_direction",
|
||||||
|
"alarm", "indicator_light", "indicator", "fan_horizontal_swing",
|
||||||
|
"fan_alarm", "physical_control_locked", "alarm_trigger",
|
||||||
|
"led_disabled", "linkquality",
|
||||||
|
)
|
||||||
|
|
||||||
_DOMAIN_ALIASES = {
|
_DOMAIN_ALIASES = {
|
||||||
"binary_sensor": ["binary_sensor", "бинарн", "датчик", "датчики", "сенсор", "сенсоры"],
|
"binary_sensor": ["binary_sensor", "бинарн", "датчик", "датчики", "сенсор", "сенсоры"],
|
||||||
"sensor": ["sensor", "показател", "значение", "значения"],
|
"sensor": ["sensor", "показател", "значение", "значения"],
|
||||||
"light": ["light", "свет", "лампочк", "лампа", "люстр", "свитч"],
|
"light": ["light", "свет", "лампочк", "лампа", "люстр", "свитч", "спот", "led"],
|
||||||
"switch": ["switch", "розетк", "выключател", "реле"],
|
"switch": ["switch", "розетк", "выключател", "реле", "ёлк", "елк", "гирлянд", "телек", "тв"],
|
||||||
"climate": ["climate", "климат", "климата", "кондиционер", "термостат", "пол", "полотенц"],
|
"climate": ["climate", "климат", "климата", "кондиционер", "термостат", "пол", "полотенц"],
|
||||||
"cover": ["cover", "штор", "жалюз", "роллет", "гардин"],
|
"cover": ["cover", "штор", "жалюз", "роллет", "гардин"],
|
||||||
"media_player": ["media_player", "медиа", "плеер", "колонк", "телевизор"],
|
"media_player": ["media_player", "медиа", "плеер", "колонк", "телевизор"],
|
||||||
@@ -89,7 +102,7 @@ class Tools:
|
|||||||
},
|
},
|
||||||
"climate_status": {
|
"climate_status": {
|
||||||
"domains": ["climate", "sensor"],
|
"domains": ["climate", "sensor"],
|
||||||
"keywords": ["температур", "климат", "тепл", "холод", "кондиционер", "батаре"],
|
"keywords": ["температур", "климат", "тепл", "холод", "кондиционер", "полотенц"],
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
"domains": ["sensor", "binary_sensor"],
|
"domains": ["sensor", "binary_sensor"],
|
||||||
@@ -194,11 +207,17 @@ class Tools:
|
|||||||
attrs.get("area_id", "") or "",
|
attrs.get("area_id", "") or "",
|
||||||
]).lower()
|
]).lower()
|
||||||
|
|
||||||
|
def _is_sub_entity(self, eid: str) -> bool:
|
||||||
|
"""Проверяет, является ли switch/light sub-entity (служебной)."""
|
||||||
|
eid_lower = eid.lower()
|
||||||
|
return any(sub in eid_lower for sub in self._SWITCH_SUB_ENTITIES)
|
||||||
|
|
||||||
def _find_entities(
|
def _find_entities(
|
||||||
self,
|
self,
|
||||||
keywords: List[str],
|
keywords: List[str],
|
||||||
domain: Optional[str] = None,
|
domain: Optional[str] = None,
|
||||||
require_all: bool = False,
|
require_all: bool = False,
|
||||||
|
skip_sub_entities: bool = False,
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
states = self._get_states()
|
states = self._get_states()
|
||||||
result: List[str] = []
|
result: List[str] = []
|
||||||
@@ -206,6 +225,8 @@ class Tools:
|
|||||||
eid = s["entity_id"]
|
eid = s["entity_id"]
|
||||||
if domain and not eid.startswith(domain + "."):
|
if domain and not eid.startswith(domain + "."):
|
||||||
continue
|
continue
|
||||||
|
if skip_sub_entities and self._is_sub_entity(eid):
|
||||||
|
continue
|
||||||
haystack = self._entity_text(s)
|
haystack = self._entity_text(s)
|
||||||
if not keywords:
|
if not keywords:
|
||||||
if domain:
|
if domain:
|
||||||
@@ -226,10 +247,8 @@ class Tools:
|
|||||||
return (s.get("attributes", {}) or {}).get(key, default)
|
return (s.get("attributes", {}) or {}).get(key, default)
|
||||||
|
|
||||||
def _resolve_domain(self, cmd: str) -> Optional[str]:
|
def _resolve_domain(self, cmd: str) -> Optional[str]:
|
||||||
# Приоритет: scene > script (т.к. "сцен" подходит обоим)
|
|
||||||
for domain in ["scene", "script", "automation", "binary_sensor"]:
|
for domain in ["scene", "script", "automation", "binary_sensor"]:
|
||||||
if domain in self._DOMAIN_ALIASES:
|
for a in self._DOMAIN_ALIASES.get(domain, []):
|
||||||
for a in self._DOMAIN_ALIASES[domain]:
|
|
||||||
if a in cmd:
|
if a in cmd:
|
||||||
return domain
|
return domain
|
||||||
for domain, aliases in self._DOMAIN_ALIASES.items():
|
for domain, aliases in self._DOMAIN_ALIASES.items():
|
||||||
@@ -265,14 +284,12 @@ class Tools:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
def _format_entity(self, s: Dict[str, Any]) -> str:
|
def _format_entity(self, s: Dict[str, Any]) -> str:
|
||||||
"""Форматирует сущность для вывода. Пробел между state и unit."""
|
|
||||||
attrs = s.get("attributes", {}) or {}
|
attrs = s.get("attributes", {}) or {}
|
||||||
fn = attrs.get("friendly_name", s["entity_id"])
|
fn = attrs.get("friendly_name", s["entity_id"])
|
||||||
state = s.get("state", "?")
|
state = s.get("state", "?")
|
||||||
unit = attrs.get("unit_of_measurement", "")
|
unit = attrs.get("unit_of_measurement", "")
|
||||||
area = attrs.get("area", "") or attrs.get("area_id", "")
|
area = attrs.get("area", "") or attrs.get("area_id", "")
|
||||||
room = " (" + area + ")" if area else ""
|
room = " (" + area + ")" if area else ""
|
||||||
# Разделяем state и unit пробелом только если оба непустые
|
|
||||||
if state and unit and unit not in str(state):
|
if state and unit and unit not in str(state):
|
||||||
state_str = str(state) + " " + str(unit)
|
state_str = str(state) + " " + str(unit)
|
||||||
else:
|
else:
|
||||||
@@ -338,10 +355,13 @@ class Tools:
|
|||||||
return ["свет не найден"]
|
return ["свет не найден"]
|
||||||
on_items: List[str] = []
|
on_items: List[str] = []
|
||||||
off_items: List[str] = []
|
off_items: List[str] = []
|
||||||
|
unavailable_items: List[str] = []
|
||||||
for eid in entities:
|
for eid in entities:
|
||||||
state = self._get_state(eid)
|
state = self._get_state(eid)
|
||||||
if state == "on":
|
if state == "on":
|
||||||
on_items.append(eid)
|
on_items.append(eid)
|
||||||
|
elif state == "unavailable":
|
||||||
|
unavailable_items.append(eid)
|
||||||
else:
|
else:
|
||||||
off_items.append(eid)
|
off_items.append(eid)
|
||||||
lines: List[str] = []
|
lines: List[str] = []
|
||||||
@@ -354,6 +374,8 @@ class Tools:
|
|||||||
break
|
break
|
||||||
if off_items:
|
if off_items:
|
||||||
lines.append("**Выключено:** " + str(len(off_items)) + " шт.")
|
lines.append("**Выключено:** " + str(len(off_items)) + " шт.")
|
||||||
|
if unavailable_items:
|
||||||
|
lines.append("**Недоступно:** " + str(len(unavailable_items)) + " шт.")
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _monitor_climate(self, tokens: List[str]) -> List[str]:
|
def _monitor_climate(self, tokens: List[str]) -> List[str]:
|
||||||
@@ -511,17 +533,24 @@ class Tools:
|
|||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _monitor_switches(self, tokens: List[str]) -> List[str]:
|
def _monitor_switches(self, tokens: List[str]) -> List[str]:
|
||||||
"""Какие розетки/реле включены."""
|
"""Какие розетки/реле включены (без sub-entities)."""
|
||||||
cfg = self._MONITOR_DOMAINS["switches"]
|
cfg = self._MONITOR_DOMAINS["switches"]
|
||||||
entities = self._find_entities(tokens if tokens else cfg["keywords"], "switch")
|
entities = self._find_entities(
|
||||||
|
tokens if tokens else cfg["keywords"],
|
||||||
|
"switch",
|
||||||
|
skip_sub_entities=True,
|
||||||
|
)
|
||||||
if not entities:
|
if not entities:
|
||||||
return ["розетки не найдены"]
|
return ["розетки не найдены"]
|
||||||
on_items: List[str] = []
|
on_items: List[str] = []
|
||||||
off_items: List[str] = []
|
off_items: List[str] = []
|
||||||
|
unavailable_items: List[str] = []
|
||||||
for eid in entities:
|
for eid in entities:
|
||||||
state = self._get_state(eid)
|
state = self._get_state(eid)
|
||||||
if state == "on":
|
if state == "on":
|
||||||
on_items.append(eid)
|
on_items.append(eid)
|
||||||
|
elif state == "unavailable":
|
||||||
|
unavailable_items.append(eid)
|
||||||
else:
|
else:
|
||||||
off_items.append(eid)
|
off_items.append(eid)
|
||||||
lines: List[str] = []
|
lines: List[str] = []
|
||||||
@@ -534,10 +563,33 @@ class Tools:
|
|||||||
break
|
break
|
||||||
if off_items:
|
if off_items:
|
||||||
lines.append("**Выключено:** " + str(len(off_items)) + " шт.")
|
lines.append("**Выключено:** " + str(len(off_items)) + " шт.")
|
||||||
|
if unavailable_items:
|
||||||
|
lines.append("**Недоступно:** " + str(len(unavailable_items)) + " шт.")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def _monitor_scenes(self) -> List[str]:
|
||||||
|
"""Список сцен с последним временем активации."""
|
||||||
|
states = self._get_states()
|
||||||
|
scenes = [s for s in states if s["entity_id"].startswith("scene.")]
|
||||||
|
if not scenes:
|
||||||
|
return ["Сцены не найдены"]
|
||||||
|
lines = ["**Сцены:**"]
|
||||||
|
for s in scenes:
|
||||||
|
eid = s["entity_id"]
|
||||||
|
fn = self._attr(s, "friendly_name", eid)
|
||||||
|
state = s.get("state", "")
|
||||||
|
attrs = s.get("attributes", {}) or {}
|
||||||
|
# Для сцен state содержит timestamp последней активации или "unknown"
|
||||||
|
last_activated = attrs.get("last_activated", "")
|
||||||
|
description = ""
|
||||||
|
if state == "unknown":
|
||||||
|
description = " (ни разу не активировалась)"
|
||||||
|
elif last_activated:
|
||||||
|
description = " — последняя: " + str(last_activated)[:19]
|
||||||
|
lines.append(" * " + fn + description)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _monitor_system(self) -> List[str]:
|
def _monitor_system(self) -> List[str]:
|
||||||
"""Состояние технической инфраструктуры: ноды, бэкапы, обновления, мосты."""
|
|
||||||
states = self._get_states()
|
states = self._get_states()
|
||||||
items: List[str] = []
|
items: List[str] = []
|
||||||
warnings: List[str] = []
|
warnings: List[str] = []
|
||||||
@@ -556,7 +608,6 @@ class Tools:
|
|||||||
text = (eid + " " + name).lower()
|
text = (eid + " " + name).lower()
|
||||||
|
|
||||||
if any(k in text for k in keys_backup):
|
if any(k in text for k in keys_backup):
|
||||||
# Backups Stale: off = всё ОК
|
|
||||||
if cur == "on":
|
if cur == "on":
|
||||||
warnings.append("Бэкапы устарели: " + self._attr(s, "friendly_name", eid))
|
warnings.append("Бэкапы устарели: " + self._attr(s, "friendly_name", eid))
|
||||||
continue
|
continue
|
||||||
@@ -567,7 +618,6 @@ class Tools:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if any(k in text for k in keys_node):
|
if any(k in text for k in keys_node):
|
||||||
# Node kybinka Status: on = OK
|
|
||||||
if cur == "off":
|
if cur == "off":
|
||||||
warnings.append("Нода offline: " + self._attr(s, "friendly_name", eid))
|
warnings.append("Нода offline: " + self._attr(s, "friendly_name", eid))
|
||||||
else:
|
else:
|
||||||
@@ -613,6 +663,8 @@ class Tools:
|
|||||||
lines.append("")
|
lines.append("")
|
||||||
lines.extend(self._monitor_battery([]))
|
lines.extend(self._monitor_battery([]))
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
lines.extend(self._monitor_scenes())
|
||||||
|
lines.append("")
|
||||||
lines.extend(self._monitor_system())
|
lines.extend(self._monitor_system())
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
@@ -683,16 +735,13 @@ class Tools:
|
|||||||
if is_question:
|
if is_question:
|
||||||
matched = False
|
matched = False
|
||||||
|
|
||||||
# ВАЖНО: порядок блоков влияет на маршрутизацию.
|
|
||||||
# Более специфичные ключевые слова — первыми.
|
|
||||||
|
|
||||||
if any(k in cmd for k in ["окн", "двер", "форточ", "калитк", "ворота", "гараж"]):
|
if any(k in cmd for k in ["окн", "двер", "форточ", "калитк", "ворота", "гараж"]):
|
||||||
actions.extend(self._monitor_windows_doors(tokens))
|
actions.extend(self._monitor_windows_doors(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
elif any(k in cmd for k in ["розетк", "выключател", "реле"]):
|
elif any(k in cmd for k in ["розетк", "выключател", "реле"]):
|
||||||
actions.extend(self._monitor_switches(tokens))
|
actions.extend(self._monitor_switches(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
elif any(k in cmd for k in ["свет", "лампочк", "люстр"]) and (
|
elif any(k in cmd for k in ["свет", "лампочк", "люстр", "спот", "led"]) and (
|
||||||
"горит" in cmd or "включ" in cmd or "лишн" in cmd
|
"горит" in cmd or "включ" in cmd or "лишн" in cmd
|
||||||
):
|
):
|
||||||
actions.extend(self._monitor_lights(tokens))
|
actions.extend(self._monitor_lights(tokens))
|
||||||
@@ -703,7 +752,6 @@ class Tools:
|
|||||||
elif any(k in cmd for k in ["движен", "двигает", "хот", "кто-то ход"]):
|
elif any(k in cmd for k in ["движен", "двигает", "хот", "кто-то ход"]):
|
||||||
actions.extend(self._monitor_motion(tokens))
|
actions.extend(self._monitor_motion(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
# БАТАРЕЙКИ — раньше климата! иначе "батарейки" → "батаре" → климат
|
|
||||||
elif "батарейк" in cmd or "аккумулятор" in cmd or "заряд" in cmd:
|
elif "батарейк" in cmd or "аккумулятор" in cmd or "заряд" in cmd:
|
||||||
actions.extend(self._monitor_battery(tokens))
|
actions.extend(self._monitor_battery(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
@@ -713,6 +761,9 @@ class Tools:
|
|||||||
elif any(k in cmd for k in ["музык", "медиа", "плеер", "колонк", "телевизор"]):
|
elif any(k in cmd for k in ["музык", "медиа", "плеер", "колонк", "телевизор"]):
|
||||||
actions.extend(self._monitor_media(tokens))
|
actions.extend(self._monitor_media(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
|
elif "сцен" in cmd and ("какие" in cmd or "список" in cmd or "покажи" in cmd):
|
||||||
|
actions.extend(self._monitor_scenes())
|
||||||
|
matched = True
|
||||||
elif "дома" in cmd or "присутств" in cmd or "кто" in cmd:
|
elif "дома" in cmd or "присутств" in cmd or "кто" in cmd:
|
||||||
actions.extend(self._monitor_presence(tokens))
|
actions.extend(self._monitor_presence(tokens))
|
||||||
matched = True
|
matched = True
|
||||||
@@ -742,6 +793,7 @@ class Tools:
|
|||||||
" - кто дома?\n"
|
" - кто дома?\n"
|
||||||
" - батарейки садятся?\n"
|
" - батарейки садятся?\n"
|
||||||
" - что в розетках включено?\n"
|
" - что в розетках включено?\n"
|
||||||
|
" - какие сцены есть?\n"
|
||||||
" - как система? есть ли обновления?\n"
|
" - как система? есть ли обновления?\n"
|
||||||
" - покажи все binary_sensor\n"
|
" - покажи все binary_sensor\n"
|
||||||
" - переименуй automation.сертификат в Обновление SSL\n"
|
" - переименуй automation.сертификат в Обновление SSL\n"
|
||||||
@@ -770,19 +822,19 @@ class Tools:
|
|||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
actions: List[str] = []
|
actions: List[str] = []
|
||||||
|
|
||||||
if "свет" in cmd or "лампочк" in cmd:
|
if "свет" in cmd or "лампочк" in cmd or "спот" in cmd:
|
||||||
entities = self._find_entities(tokens, "light")
|
entities = self._find_entities(tokens, "light")
|
||||||
if not entities:
|
if not entities:
|
||||||
warnings.append("светильники не найдены")
|
warnings.append("светильники не найдены")
|
||||||
else:
|
else:
|
||||||
turn_off = any(w in cmd for w in ("выключ", "отключ", "погас"))
|
turn_off = any(w in cmd for w in ("выключ", "отключ", "погас"))
|
||||||
brightness = self._extract_number(cmd)
|
brightness = self._extract_number(cmd)
|
||||||
|
# Локальная команда, если указано имя, площадь или имя человека
|
||||||
is_global = not any(
|
is_global = not any(
|
||||||
t in cmd for t in [
|
t in cmd for t in [
|
||||||
"спальн", "гостин", "кухн", "детск", "ванн",
|
"спальн", "гостин", "кухн", "детск", "ванн",
|
||||||
"прихож", "коридор", "кабинет", "зал", "веранд",
|
"прихож", "коридор", "кабинет", "зал", "веранд",
|
||||||
"дима", "диан", "елен", "анатоли", "милен",
|
] + list(self._PEOPLE_NAMES)
|
||||||
]
|
|
||||||
) and len(entities) > 1
|
) and len(entities) > 1
|
||||||
|
|
||||||
if is_global and self.valves.confirm_global_actions and not self.valves.dry_run:
|
if is_global and self.valves.confirm_global_actions and not self.valves.dry_run:
|
||||||
@@ -817,8 +869,10 @@ class Tools:
|
|||||||
label = "выключен" if turn_off else "включён"
|
label = "выключен" if turn_off else "включён"
|
||||||
actions.append("свет " + label + " (" + str(count) + " шт.)")
|
actions.append("свет " + label + " (" + str(count) + " шт.)")
|
||||||
|
|
||||||
if any(k in cmd for k in ["розетк", "выключател", "устройств", "реле"]):
|
if any(k in cmd for k in ["розетк", "выключател", "устройств", "реле", "ёлк", "елк", "гирлянд", "телек"]):
|
||||||
entities = self._find_entities(tokens, "switch")
|
entities = self._find_entities(
|
||||||
|
tokens, "switch", skip_sub_entities=True,
|
||||||
|
)
|
||||||
if not entities:
|
if not entities:
|
||||||
warnings.append("розетки не найдены")
|
warnings.append("розетки не найдены")
|
||||||
else:
|
else:
|
||||||
@@ -989,7 +1043,6 @@ class Tools:
|
|||||||
else:
|
else:
|
||||||
warnings.append("кнопки не найдены")
|
warnings.append("кнопки не найдены")
|
||||||
|
|
||||||
# Сцены (домен scene — мгновенное применение)
|
|
||||||
if any(k in cmd for k in ["сцен"]) and "запуст" in cmd:
|
if any(k in cmd for k in ["сцен"]) and "запуст" in cmd:
|
||||||
all_scenes = self._find_entities([], "scene")
|
all_scenes = self._find_entities([], "scene")
|
||||||
matched: List[str] = []
|
matched: List[str] = []
|
||||||
@@ -1006,7 +1059,6 @@ class Tools:
|
|||||||
else:
|
else:
|
||||||
warnings.append("подходящая сцена не найдена среди " + str(len(all_scenes)))
|
warnings.append("подходящая сцена не найдена среди " + str(len(all_scenes)))
|
||||||
|
|
||||||
# Скрипты (run-once)
|
|
||||||
if "скрипт" in cmd or "запуст" in cmd:
|
if "скрипт" in cmd or "запуст" in cmd:
|
||||||
all_scripts = self._find_entities([], "script")
|
all_scripts = self._find_entities([], "script")
|
||||||
matched = []
|
matched = []
|
||||||
@@ -1035,6 +1087,7 @@ class Tools:
|
|||||||
only_with_state: str = "",
|
only_with_state: str = "",
|
||||||
show_unavailable: bool = True,
|
show_unavailable: bool = True,
|
||||||
limit: int = 50,
|
limit: int = 50,
|
||||||
|
skip_sub_entities: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
if not self.valves.ha_token:
|
if not self.valves.ha_token:
|
||||||
return "Не задан HA Token (Valves)."
|
return "Не задан HA Token (Valves)."
|
||||||
@@ -1064,6 +1117,8 @@ class Tools:
|
|||||||
if not eid.startswith(domain + "."):
|
if not eid.startswith(domain + "."):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if skip_sub_entities and self._is_sub_entity(eid):
|
||||||
|
continue
|
||||||
if device_class and dc != device_class:
|
if device_class and dc != device_class:
|
||||||
continue
|
continue
|
||||||
if only_with_state and cur_state != only_with_state:
|
if only_with_state and cur_state != only_with_state:
|
||||||
|
|||||||
Reference in New Issue
Block a user