From 45dc5399ad9bc1f0047ec7a38f72f1ae251396b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C6=B0u=20Quang=20V=C5=A9?= Date: Fri, 4 Sep 2026 15:07:14 +0700 Subject: [PATCH 1/2] Add pre-commit validation hooks and update missing docstrings --- .gitignore | 3 +- .pre-commit-config.yaml | 44 +++++++++++++++++++++++++++++ package.json | 11 ++++++++ pyproject.toml | 23 +++++++++++---- scripts/common_utilities.py | 15 ++++++++++ scripts/date_conversion_tool.py | 9 +++--- scripts/memory.py | 5 ++-- scripts/telegram_bot_handle_tool.py | 2 ++ scripts/youtube_data_tool.py | 24 ++++++++++++++++ scripts/zalo_bot_handle_tool.py | 2 ++ 10 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 54c65ea..bc45a0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -.env .venv/ .vscode/ __pycache__/ .ruff_cache/ +node_modules/ uv.lock +package-lock.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5cf00bf --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +repos: + - repo: local + hooks: + - id: ruff-check + name: Run Ruff Check + entry: uv run ruff check --fix + language: system + pass_filenames: false + always_run: true + + - id: ruff-format + name: Run Ruff Format + entry: uv run ruff format + language: system + pass_filenames: false + always_run: true + + - id: ty-check + name: Run Ty Check + entry: uv run ty check + language: system + pass_filenames: false + always_run: true + + - id: interrogate + name: Run Interrogate + entry: uv run interrogate + language: system + pass_filenames: false + always_run: true + + - id: prettier + name: Run Prettier + entry: npx prettier --write . + language: system + pass_filenames: false + always_run: true + + - id: blueprints-check + name: Run Blueprints Check + entry: uv run tools/validate_blueprints.py + language: system + pass_filenames: false + always_run: true diff --git a/package.json b/package.json new file mode 100644 index 0000000..87070aa --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "devDependencies": { + "prettier": "^3.9.6", + "prettier-plugin-toml": "^2.0.6" + }, + "prettier": { + "plugins": [ + "prettier-plugin-toml" + ] + } +} diff --git a/pyproject.toml b/pyproject.toml index c1f4309..b7767f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,8 @@ dev = [ "google-api-python-client", "homeassistant", "httpx[http2]", + "interrogate", + "prek", "ruff", "ty", ] @@ -24,18 +26,29 @@ target-version = "py313" [tool.ruff.lint] select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions "E", # pycodestyle errors "F", # pyflakes - "W", # pycodestyle warnings "I", # isort - "UP", # pyupgrade - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "SIM", # flake8-simplify + "LOG", # flake8-logging + "N", # pep8-naming + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "RET", # flake8-return "RUF", # ruff-specific rules + "SIM", # flake8-simplify "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle warnings ] +[tool.ruff.lint.pydocstyle] +convention = "google" + [tool.ruff.format] quote-style = "double" indent-style = "space" + +[tool.interrogate] +fail-under = 100 diff --git a/scripts/common_utilities.py b/scripts/common_utilities.py index aac84de..3ccacb5 100644 --- a/scripts/common_utilities.py +++ b/scripts/common_utilities.py @@ -1,3 +1,5 @@ +"""Common cache and locking utilities for Home Assistant Pyscript automations.""" + import asyncio import sqlite3 import threading @@ -22,12 +24,15 @@ class _IndexLockContext: + """Async context manager for acquiring named index locks safely.""" + key: str lock: asyncio.Lock | None # Pyscript handles __init__ specially; initialize these attributes in the factory below. @pyscript_compile # noqa: F821 # ty:ignore[unresolved-reference] async def __aenter__(self): + """Acquire the index lock asynchronously.""" key = self.key with _INDEX_LOCKS_GUARD: if key not in _INDEX_LOCKS: @@ -41,6 +46,7 @@ async def __aenter__(self): @pyscript_compile # noqa: F821 # ty:ignore[unresolved-reference] async def __aexit__(self, exc_type, exc_val, exc_tb): + """Release the index lock and clean up unused locks.""" if self.lock: self.lock.release() @@ -54,6 +60,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): def _acquire_index_lock(key: str): + """Create a context manager for acquiring a named index lock.""" context = _IndexLockContext() context.key = key context.lock = None @@ -475,6 +482,14 @@ async def memory_cache_index_update( } def _normalize(_value: Any) -> list[str]: + """Normalize input value into a list of non-empty strings. + + Args: + _value: Input data which may be a primitive, list, tuple, or set. + + Returns: + List of stripped strings extracted from the input value. + """ if _value is None: return [] if isinstance(_value, (str, int, float, bool)): diff --git a/scripts/date_conversion_tool.py b/scripts/date_conversion_tool.py index 42b9287..bd402c8 100644 --- a/scripts/date_conversion_tool.py +++ b/scripts/date_conversion_tool.py @@ -83,8 +83,7 @@ def sun_longitude(jdn: float) -> float: dl += (0.019993 - 0.000101 * t) * math.sin(dr * 2 * m) + 0.000290 * math.sin(dr * 3 * m) longitude = l0 + dl longitude = longitude * dr - longitude = longitude - math.pi * 2 * (int(longitude / (math.pi * 2))) - return longitude + return longitude - math.pi * 2 * (int(longitude / (math.pi * 2))) @pyscript_compile # noqa: F821 # ty:ignore[unresolved-reference] @@ -185,7 +184,7 @@ def lunar_to_solar( leap_month += 12 if lunar_leap != 0 and lunar_month != leap_month: return [0, 0, 0] - elif lunar_leap != 0 or off >= leap_off: + if lunar_leap != 0 or off >= leap_off: off += 1 month_start = get_new_moon_day(k + off, time_zone) return jd_to_date(month_start + lunar_day - 1) @@ -965,9 +964,9 @@ def get_auspicious_day(lunar_month: int, jd: int) -> dict[str, Any]: if AUSPICIOUS_DAY_STATUS[offset] == "T": return {"day_type": "hoang_dao", "name": "Ngày Hoàng Đạo"} - elif AUSPICIOUS_DAY_STATUS[offset] == "X": + if AUSPICIOUS_DAY_STATUS[offset] == "X": return {"day_type": "hac_dao", "name": "Ngày Hắc Đạo"} - elif AUSPICIOUS_DAY_STATUS[offset] == "B": + if AUSPICIOUS_DAY_STATUS[offset] == "B": return { "day_type": "neutral", "name": "Ngày trung bình (Không tốt không xấu)", diff --git a/scripts/memory.py b/scripts/memory.py index 7c38a58..79f3b23 100644 --- a/scripts/memory.py +++ b/scripts/memory.py @@ -1,3 +1,5 @@ +"""Persistent memory and semantic retrieval engine for Home Assistant Pyscript automations.""" + import asyncio import re import sqlite3 @@ -245,8 +247,7 @@ def _normalize_key(s: str) -> str: s = s.strip().lower() s = _strip_diacritics(s) s = re.sub(r"[^a-z0-9_]", "_", s) - s = re.sub(r"_+", "_", s).strip("_") - return s + return re.sub(r"_+", "_", s).strip("_") def _condense_candidate_for_selection(entry: dict[str, Any], *, score: float | None = None) -> dict[str, Any]: diff --git a/scripts/telegram_bot_handle_tool.py b/scripts/telegram_bot_handle_tool.py index 2ef1c85..0004b8e 100644 --- a/scripts/telegram_bot_handle_tool.py +++ b/scripts/telegram_bot_handle_tool.py @@ -1,3 +1,5 @@ +"""Telegram Bot integration tools and service handlers for Home Assistant.""" + import asyncio import contextlib import mimetypes diff --git a/scripts/youtube_data_tool.py b/scripts/youtube_data_tool.py index f8990d5..5c941ea 100644 --- a/scripts/youtube_data_tool.py +++ b/scripts/youtube_data_tool.py @@ -1,3 +1,5 @@ +"""YouTube Data API tools for searching videos, playlists, and channels in Home Assistant.""" + import asyncio from typing import Any @@ -98,6 +100,17 @@ async def youtube_search_tool(query: str, **kwargs) -> dict[str, Any]: return {"error": "Missing a required argument: query"} def _coerce_results(value: Any) -> int: + """Coerce and validate the requested search results count. + + Args: + value: Raw results limit from service call parameters. + + Returns: + Validated integer count between 1 and 50. + + Raises: + ValueError: If the value cannot be coerced to an integer or is out of range. + """ try: coerced = int(value) except (TypeError, ValueError) as err: @@ -107,6 +120,17 @@ def _coerce_results(value: Any) -> int: return coerced def _coerce_search_types(value: Any) -> list[str]: + """Normalize and filter requested search types. + + Args: + value: Search type string, list, tuple, or set of types. + + Returns: + List of valid lowercase search types (video, channel, playlist). + + Raises: + ValueError: If the value is not a supported string or sequence. + """ valid_types = {"video", "channel", "playlist"} if value is None: items: list[str] = [] diff --git a/scripts/zalo_bot_handle_tool.py b/scripts/zalo_bot_handle_tool.py index e50f35b..f8ec5a0 100644 --- a/scripts/zalo_bot_handle_tool.py +++ b/scripts/zalo_bot_handle_tool.py @@ -1,3 +1,5 @@ +"""Zalo Bot integration tools and service handlers for Home Assistant.""" + import asyncio import contextlib import ipaddress From f6a602e2f376d32fe695affde6d6c8f41b5d7e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C6=B0u=20Quang=20V=C5=A9?= Date: Fri, 4 Sep 2026 15:57:14 +0700 Subject: [PATCH 2/2] Fix minor typos and formatting inconsistencies across tutorials and documentation --- README.md | 46 +++++++++---------- README_vi.md | 46 +++++++++---------- check_the_device_turns_on_manually.yaml | 12 ++++- create_calendar_event_full_llm.yaml | 2 +- create_lunar_events.yaml | 2 +- date_lookup_and_conversion_full_llm.yaml | 2 +- devices_schedules.yaml | 4 +- devices_schedules_controller_full_llm.yaml | 2 +- devices_schedules_restart_handler.yaml | 2 +- get_youtube_video_info_full_llm.yaml | 2 +- ...sistant_device_location_lookup_guide_en.md | 22 +++++---- ...sistant_device_location_lookup_guide_vi.md | 22 +++++---- home_assistant_ios_themes_en.md | 19 ++++---- home_assistant_ios_themes_vi.md | 17 +++---- ...play_favorite_youtube_channel_videos_en.md | 19 ++++---- ...play_favorite_youtube_channel_videos_vi.md | 19 ++++---- home_assistant_unavailable_devices_en.md | 29 ++++++------ home_assistant_unavailable_devices_vi.md | 29 ++++++------ 18 files changed, 160 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 96b4d39..439df8c 100644 --- a/README.md +++ b/README.md @@ -184,29 +184,29 @@ Some blueprints connect to specific Home Assistant services or third-party APIs: Use this quick reference table to find the exact requirements for each blueprint: -| Blueprint | Type | Companion Blueprints Needed | Required Modules | Python Scripts & Secrets | -| ------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| [Smart Scheduling & Timers](#voice-assist---smart-scheduling--timers) | Script + Automations | Controller + Core (`devices_schedules.yaml`) + Restart (`devices_schedules_restart_handler.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `common_utilities.py` | -| [Memory Tool (LLM)](#voice-assist---memory--information-retrieval) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `memory.py` | -| [Memory Tool (Local)](#voice-assist---memory--information-retrieval) | Automation | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `memory.py`, `common_utilities.py` | -| [Camera Image Analysis](#voice-assist---camera-image-analysis) | Scripts | Snapshot + Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 3](#module-3-specialized-integrations--external-services) | AI Task entity, `/media` storage folder | -| [Create Calendar Events](#create-calendar-events) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Calendar with Read/Write access | -| [Calendar Events Lookup](#calendar-events-lookup) | Script | None | None | Configured Calendar entity | -| [Lunar Calendar Conversion & Lookup](#lunar-calendar-conversion--lookup) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `date_conversion_tool.py` | -| [Create Lunar Calendar Events](#create-lunar-calendar-events) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts), [Module 3](#module-3-specialized-integrations--external-services) | `date_conversion_tool.py`, Calendar with Read/Write access | -| [Interactive Chatbot (Telegram)](#interactive-smart-home-chatbot) | Automation | Optional: Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 2](#module-2-pyscript-integration--helper-scripts) | `telegram_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Telegram Bot Token | -| [Interactive Chatbot (Zalo)](#interactive-smart-home-chatbot) | Automation | Optional: Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 2](#module-2-pyscript-integration--helper-scripts) | `zalo_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Zalo Bot Token | -| [Send to Telegram](#voice-assist---send-messages--media) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `telegram_bot_handle_tool.py`, `requirements.txt`, Telegram Bot Token | -| [Send to Zalo](#voice-assist---send-messages--media) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `zalo_bot_handle_tool.py`, `requirements.txt`, Zalo Bot Token | -| [Internet Knowledge Search](#voice-assist---internet-knowledge-search) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Gemini Agent with Google Search & 16k+ tokens | -| [YouTube Search & Playback](#voice-assist---youtube-search--playback) | Scripts | Search + Player (`play_youtube_video_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `youtube_data_tool.py`, `requirements.txt`, YouTube API Key, TV YouTube App | -| [Favorite YouTube Channels](#voice-assist---favorite-youtube-channels) | Scripts | Info Getter + Player (`play_youtube_video_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `youtube_data_tool.py`, `requirements.txt`, YouTube API Key, TV YouTube App | -| [Smart Fan Control](#voice-assist---smart-fan-control) | Script | None | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup) | Fan entities exposed to Assist | -| [Smart AC Control](#voice-assist---smart-ac-control) | Script | None | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup) | Climate entity exposed to Assist | -| [Weather Forecast](#voice-assist---weather-forecast) | Script | None | None | Weather entity with hourly & daily forecasts | -| [Music Control](#voice-assist---music-control) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Music Assistant integration | -| [Device Location & Find](#voice-assist---device-location--find) | Scripts | Location Lookup + Ringing (`device_ringing_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 3](#module-3-specialized-integrations--external-services) | Bermuda / Mobile app tracker, HA Companion App notifications | -| [Device State Synchronization](#device-state-synchronization) | Automation | None | None | Controllable switch/light entities | +| Blueprint | Type | Companion Blueprints Needed | Required Modules | Python Scripts & Secrets | +| ------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| [Smart Scheduling & Timers](#voice-assist---smart-scheduling--timers) | Script + Automations | Controller (`devices_schedules_controller_full_llm.yaml`) + Core (`devices_schedules.yaml`) + Restart (`devices_schedules_restart_handler.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `common_utilities.py` | +| [Memory Tool (LLM)](#voice-assist---memory--information-retrieval) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `memory.py` | +| [Memory Tool (Local)](#voice-assist---memory--information-retrieval) | Automation | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `memory.py`, `common_utilities.py` | +| [Camera Image Analysis](#voice-assist---camera-image-analysis) | Scripts | Snapshot (`camera_snapshot_full_llm.yaml`) + Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 3](#module-3-specialized-integrations--external-services) | AI Task entity, `/media` storage folder | +| [Create Calendar Events](#create-calendar-events) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Calendar with Read/Write access | +| [Calendar Events Lookup](#calendar-events-lookup) | Script | None | None | Configured Calendar entity | +| [Lunar Calendar Conversion & Lookup](#lunar-calendar-conversion--lookup) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `date_conversion_tool.py` | +| [Create Lunar Calendar Events](#create-lunar-calendar-events) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts), [Module 3](#module-3-specialized-integrations--external-services) | `date_conversion_tool.py`, Calendar with Read/Write access | +| [Interactive Chatbot (Telegram)](#interactive-smart-home-chatbot) | Automation | Optional: Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 2](#module-2-pyscript-integration--helper-scripts) | `telegram_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Telegram Bot Token | +| [Interactive Chatbot (Zalo)](#interactive-smart-home-chatbot) | Automation | Optional: Analyzer (`file_content_analyzer_full_llm.yaml`) | [Module 2](#module-2-pyscript-integration--helper-scripts) | `zalo_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Zalo Bot Token | +| [Send to Telegram](#voice-assist---send-messages--media) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `telegram_bot_handle_tool.py`, `requirements.txt`, Telegram Bot Token | +| [Send to Zalo](#voice-assist---send-messages--media) | Script | None | [Module 2](#module-2-pyscript-integration--helper-scripts) | `zalo_bot_handle_tool.py`, `requirements.txt`, Zalo Bot Token | +| [Internet Knowledge Search](#voice-assist---internet-knowledge-search) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Gemini Agent with Google Search & 16k+ tokens | +| [YouTube Search & Playback](#voice-assist---youtube-search--playback) | Scripts | Search (`advanced_youtube_search_full_llm.yaml`) + Player (`play_youtube_video_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `youtube_data_tool.py`, `requirements.txt`, YouTube API Key, TV YouTube App | +| [Favorite YouTube Channels](#voice-assist---favorite-youtube-channels) | Scripts | Info Getter (`get_youtube_video_info_full_llm.yaml`) + Player (`play_youtube_video_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 2](#module-2-pyscript-integration--helper-scripts) | `youtube_data_tool.py`, `requirements.txt`, YouTube API Key, TV YouTube App | +| [Smart Fan Control](#voice-assist---smart-fan-control) | Script | None | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup) | Fan entities exposed to Assist | +| [Smart AC Control](#voice-assist---smart-ac-control) | Script | None | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup) | Climate entity exposed to Assist | +| [Weather Forecast](#voice-assist---weather-forecast) | Script | None | None | Weather entity with hourly & daily forecasts | +| [Music Control](#voice-assist---music-control) | Script | None | [Module 3](#module-3-specialized-integrations--external-services) | Music Assistant integration | +| [Device Location & Find](#voice-assist---device-location--find) | Scripts | Location Lookup (`device_location_lookup_full_llm.yaml`) + Ringing (`device_ringing_full_llm.yaml`) | [Module 1](#module-1-entity-aliases-sensor-friendly-name-lookup), [Module 3](#module-3-specialized-integrations--external-services) | Bermuda / Mobile app tracker, HA Companion App notifications | +| [Device State Synchronization](#device-state-synchronization) | Automation | None | None | Controllable switch/light entities | --- diff --git a/README_vi.md b/README_vi.md index 55e56c7..9b53921 100644 --- a/README_vi.md +++ b/README_vi.md @@ -184,29 +184,29 @@ Một số blueprint yêu cầu kết nối tới các dịch vụ tích hợp s Bảng tóm tắt nhanh các thành phần cần chuẩn bị cho từng blueprint: -| Blueprint | Loại | Blueprint Đi kèm Cần thiết | Mô-đun Bắt buộc | Script Python & Bí mật Cần có | -| ---------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| [Hẹn giờ & Lên lịch Thông minh](#voice-assist---hẹn-giờ--lên-lịch-thông-minh) | Kịch bản + Tự động hóa | Điều khiển (Controller) + Lõi (`devices_schedules.yaml`) + Khởi động lại (`devices_schedules_restart_handler.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `common_utilities.py` | -| [Ghi nhớ và Truy xuất (LLM)](#voice-assist---ghi-nhớ-và-truy-xuất-thông-tin) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `memory.py` | -| [Ghi nhớ và Truy xuất (Cục bộ)](#voice-assist---ghi-nhớ-và-truy-xuất-thông-tin) | Tự động hóa | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `memory.py`, `common_utilities.py` | -| [Phân tích Hình ảnh Camera](#voice-assist---phân-tích-hình-ảnh-camera) | Kịch bản | Chụp ảnh (Snapshot) + Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Thực thể AI Task, thư mục `/media` | -| [Tạo Sự kiện Lịch](#tạo-sự-kiện-lịch) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Lịch có quyền Đọc/Ghi | -| [Tra cứu Sự kiện trong Lịch](#tra-cứu-sự-kiện-trong-lịch) | Kịch bản | Không | Không | Thực thể Lịch đã cấu hình | -| [Tra cứu & Chuyển đổi Lịch Âm](#tra-cứu--chuyển-đổi-lịch-âm) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `date_conversion_tool.py` | -| [Tạo Sự kiện theo Lịch Âm](#tạo-sự-kiện-theo-lịch-âm) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | `date_conversion_tool.py`, Lịch có quyền Đọc/Ghi | -| [Chatbot Tương tác (Telegram)](#chatbot-tương-tác--điều-khiển-nhà-thông-minh) | Tự động hóa | Tùy chọn: Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `telegram_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Token Bot Telegram | -| [Chatbot Tương tác (Zalo)](#chatbot-tương-tác--điều-khiển-nhà-thông-minh) | Tự động hóa | Tùy chọn: Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `zalo_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Token Bot Zalo | -| [Gửi Tin nhắn Telegram](#voice-assist---gửi-tin-nhắn--nội-dung) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `telegram_bot_handle_tool.py`, `requirements.txt`, Token Bot Telegram | -| [Gửi Tin nhắn Zalo](#voice-assist---gửi-tin-nhắn--nội-dung) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `zalo_bot_handle_tool.py`, `requirements.txt`, Token Bot Zalo | -| [Tra cứu Thông tin Internet](#voice-assist---tra-cứu-thông-tin-internet) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Agent Gemini có Google Search & tối thiểu 16k token | -| [Tìm kiếm & Phát YouTube](#voice-assist---tìm-kiếm--phát-video-youtube) | Kịch bản | Tìm kiếm + Phát (`play_youtube_video_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `youtube_data_tool.py`, `requirements.txt`, Khóa YouTube API, Ứng dụng YouTube trên TV | -| [Theo dõi Kênh YouTube Yêu thích](#voice-assist---theo-dõi-kênh-youtube-yêu-thích) | Kịch bản | Lấy thông tin + Phát (`play_youtube_video_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `youtube_data_tool.py`, `requirements.txt`, Khóa YouTube API, Ứng dụng YouTube trên TV | -| [Điều khiển Quạt Thông minh](#voice-assist---điều-khiển-quạt-thông-minh) | Kịch bản | Không | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện) | Thực thể quạt bộc lộ cho Assist | -| [Điều khiển Điều hòa Thông minh](#voice-assist---điều-khiển-điều-hòa-thông-minh) | Kịch bản | Không | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện) | Thực thể điều hòa bộc lộ cho Assist | -| [Dự báo Thời tiết](#voice-assist---dự-báo-thời-tiết) | Kịch bản | Không | Không | Thực thể thời tiết có dự báo theo giờ & ngày | -| [Điều khiển Nhạc](#voice-assist---điều-khiển-nhạc) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Tích hợp Music Assistant | -| [Định vị & Tìm kiếm Thiết bị](#voice-assist---định-vị--tìm-kiếm-thiết-bị) | Kịch bản | Tìm vị trí + Đổ chuông (`device_ringing_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Thiết bị theo dõi Bermuda / Mobile app, Thông báo ứng dụng HA Companion | -| [Đồng bộ Trạng thái Thiết bị](#đồng-bộ-trạng-thái-thiết-bị) | Tự động hóa | Không | Không | Các thực thể công tắc / đèn điều khiển được | +| Blueprint | Loại | Blueprint Đi kèm Cần thiết | Mô-đun Bắt buộc | Script Python & Bí mật Cần có | +| ---------------------------------------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| [Hẹn giờ & Lên lịch Thông minh](#voice-assist---hẹn-giờ--lên-lịch-thông-minh) | Kịch bản + Tự động hóa | Điều khiển (`devices_schedules_controller_full_llm.yaml`) + Lõi (`devices_schedules.yaml`) + Khởi động lại (`devices_schedules_restart_handler.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `common_utilities.py` | +| [Ghi nhớ và Truy xuất (LLM)](#voice-assist---ghi-nhớ-và-truy-xuất-thông-tin) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `memory.py` | +| [Ghi nhớ và Truy xuất (Cục bộ)](#voice-assist---ghi-nhớ-và-truy-xuất-thông-tin) | Tự động hóa | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `memory.py`, `common_utilities.py` | +| [Phân tích Hình ảnh Camera](#voice-assist---phân-tích-hình-ảnh-camera) | Kịch bản | Chụp ảnh (`camera_snapshot_full_llm.yaml`) + Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Thực thể AI Task, thư mục `/media` | +| [Tạo Sự kiện Lịch](#tạo-sự-kiện-lịch) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Lịch có quyền Đọc/Ghi | +| [Tra cứu Sự kiện trong Lịch](#tra-cứu-sự-kiện-trong-lịch) | Kịch bản | Không | Không | Thực thể Lịch đã cấu hình | +| [Tra cứu & Chuyển đổi Lịch Âm](#tra-cứu--chuyển-đổi-lịch-âm) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `date_conversion_tool.py` | +| [Tạo Sự kiện theo Lịch Âm](#tạo-sự-kiện-theo-lịch-âm) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | `date_conversion_tool.py`, Lịch có quyền Đọc/Ghi | +| [Chatbot Tương tác (Telegram)](#chatbot-tương-tác--điều-khiển-nhà-thông-minh) | Tự động hóa | Tùy chọn: Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `telegram_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Token Bot Telegram | +| [Chatbot Tương tác (Zalo)](#chatbot-tương-tác--điều-khiển-nhà-thông-minh) | Tự động hóa | Tùy chọn: Phân tích (`file_content_analyzer_full_llm.yaml`) | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `zalo_bot_handle_tool.py`, `common_utilities.py`, `requirements.txt`, Token Bot Zalo | +| [Gửi Tin nhắn Telegram](#voice-assist---gửi-tin-nhắn--nội-dung) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `telegram_bot_handle_tool.py`, `requirements.txt`, Token Bot Telegram | +| [Gửi Tin nhắn Zalo](#voice-assist---gửi-tin-nhắn--nội-dung) | Kịch bản | Không | [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `zalo_bot_handle_tool.py`, `requirements.txt`, Token Bot Zalo | +| [Tra cứu Thông tin Internet](#voice-assist---tra-cứu-thông-tin-internet) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Agent Gemini có Google Search & tối thiểu 16k token | +| [Tìm kiếm & Phát YouTube](#voice-assist---tìm-kiếm--phát-video-youtube) | Kịch bản | Tìm kiếm (`advanced_youtube_search_full_llm.yaml`) + Phát (`play_youtube_video_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `youtube_data_tool.py`, `requirements.txt`, Khóa YouTube API, Ứng dụng YouTube trên TV | +| [Theo dõi Kênh YouTube Yêu thích](#voice-assist---theo-dõi-kênh-youtube-yêu-thích) | Kịch bản | Lấy thông tin (`get_youtube_video_info_full_llm.yaml`) + Phát (`play_youtube_video_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 2](#mô-đun-2-tích-hợp-pyscript--script-hỗ-trợ) | `youtube_data_tool.py`, `requirements.txt`, Khóa YouTube API, Ứng dụng YouTube trên TV | +| [Điều khiển Quạt Thông minh](#voice-assist---điều-khiển-quạt-thông-minh) | Kịch bản | Không | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện) | Thực thể quạt bộc lộ cho Assist | +| [Điều khiển Điều hòa Thông minh](#voice-assist---điều-khiển-điều-hòa-thông-minh) | Kịch bản | Không | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện) | Thực thể điều hòa bộc lộ cho Assist | +| [Dự báo Thời tiết](#voice-assist---dự-báo-thời-tiết) | Kịch bản | Không | Không | Thực thể thời tiết có dự báo theo giờ & ngày | +| [Điều khiển Nhạc](#voice-assist---điều-khiển-nhạc) | Kịch bản | Không | [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Tích hợp Music Assistant | +| [Định vị & Tìm kiếm Thiết bị](#voice-assist---định-vị--tìm-kiếm-thiết-bị) | Kịch bản | Tìm vị trí (`device_location_lookup_full_llm.yaml`) + Đổ chuông (`device_ringing_full_llm.yaml`) | [Mô-đun 1](#mô-đun-1-cảm-biến-bí-danh-thực-thể-tra-cứu-tên-gọi-thân-thiện), [Mô-đun 3](#mô-đun-3-các-tích-hợp--dịch-vụ-chuyên-biệt) | Thiết bị theo dõi Bermuda / Mobile app, Thông báo ứng dụng HA Companion | +| [Đồng bộ Trạng thái Thiết bị](#đồng-bộ-trạng-thái-thiết-bị) | Tự động hóa | Không | Không | Các thực thể công tắc / đèn điều khiển được | --- diff --git a/check_the_device_turns_on_manually.yaml b/check_the_device_turns_on_manually.yaml index 40a017b..4aafe69 100644 --- a/check_the_device_turns_on_manually.yaml +++ b/check_the_device_turns_on_manually.yaml @@ -5,7 +5,17 @@ blueprint: description: |- # Verify whether the device activated manually or via automation/application - - Prevents false triggers when device comes back from unavailable/unknown state. + Identifies whether a device was turned on physically by a human (e.g., pressing a physical wall switch) versus triggered programmatically by an automation, script, or user in the Home Assistant app/dashboard. + + ### Key Features: + - **Physical Activation Detection:** Uses Home Assistant event context (`parent_id` and `user_id`) to reliably differentiate physical button presses from software-initiated commands. + - **State Synchronization:** Updates an `input_boolean` helper entity (`on` when turned on manually/physically, `off` when turned off or triggered programmatically). + - **Fail-Safe Filtering:** Restricts state transitions strictly between `on` and `off`, preventing false activations when devices recover or reboot from `unavailable` or `unknown` states. + + ### Requirements: + - A target switch or light entity (`switch` or `light`). + - An `input_boolean` helper entity to reflect the manual activation state. + - Requires Home Assistant 2024.12.0 or newer. domain: automation homeassistant: min_version: 2024.12.0 diff --git a/create_calendar_event_full_llm.yaml b/create_calendar_event_full_llm.yaml index 0fd286d..d000ba2 100644 --- a/create_calendar_event_full_llm.yaml +++ b/create_calendar_event_full_llm.yaml @@ -3,7 +3,7 @@ blueprint: author: luuquangvu source_url: https://github.com/luuquangvu/tutorials/blob/dev/create_calendar_event_full_llm.yaml description: |- - # Tool help to create calendar events used for Voice Assistant + # Tool helps create calendar events used for Voice Assistant ## Blueprint Setup diff --git a/create_lunar_events.yaml b/create_lunar_events.yaml index 20e90e1..b1db4ae 100644 --- a/create_lunar_events.yaml +++ b/create_lunar_events.yaml @@ -3,7 +3,7 @@ blueprint: author: luuquangvu source_url: https://github.com/luuquangvu/tutorials/blob/dev/create_lunar_events.yaml description: |- - # Tool help to create Lunar calendar events + # Tool helps create Lunar calendar events ## Blueprint Setup diff --git a/date_lookup_and_conversion_full_llm.yaml b/date_lookup_and_conversion_full_llm.yaml index b593e84..7a86e7d 100644 --- a/date_lookup_and_conversion_full_llm.yaml +++ b/date_lookup_and_conversion_full_llm.yaml @@ -3,7 +3,7 @@ blueprint: author: luuquangvu source_url: https://github.com/luuquangvu/tutorials/blob/dev/date_lookup_and_conversion_full_llm.yaml description: |- - # Tool converts Solar date to Lunar date and vice versa used for Voice Assistant + # Tool converts Solar dates to Lunar dates and vice versa used for Voice Assistant ## Blueprint Setup diff --git a/devices_schedules.yaml b/devices_schedules.yaml index 9f0980e..fa0763a 100644 --- a/devices_schedules.yaml +++ b/devices_schedules.yaml @@ -16,8 +16,8 @@ blueprint: ### Required - The Pyscript integration needs to be installed through HACS and properly configured. - - The `scripts/common_utilities.py` script need to be copied into the `config/pyscript` folder. - - This blueprint is a dependency for the `devices_schedules_full_llm.yaml` and the `devices_schedules_restart_handler.yaml` blueprints. + - The `scripts/common_utilities.py` script needs to be copied into the `config/pyscript` folder. + - This blueprint is a dependency for the `devices_schedules_controller_full_llm.yaml` and the `devices_schedules_restart_handler.yaml` blueprints. - The mentioned file(s) is/are included in the repository. - You need to enable two Pyscript options through the UI or via YAML to allow importing all Python packages and to make hass available as a global variable. diff --git a/devices_schedules_controller_full_llm.yaml b/devices_schedules_controller_full_llm.yaml index 5d18f78..9b5e795 100644 --- a/devices_schedules_controller_full_llm.yaml +++ b/devices_schedules_controller_full_llm.yaml @@ -32,7 +32,7 @@ blueprint: ### Required - - The `devices_schedules.yaml` and `devices_schedules_restart_handler.yaml` blueprints needs to be installed. + - The `devices_schedules.yaml` and `devices_schedules_restart_handler.yaml` blueprints need to be installed. - The mentioned file(s) is/are included in the repository. - A template sensor stores all information about entity aliases needs to be configured in `config/configuration.yaml`; The sensor is required for friendly-name lookup. diff --git a/devices_schedules_restart_handler.yaml b/devices_schedules_restart_handler.yaml index 48f7242..b5deb14 100644 --- a/devices_schedules_restart_handler.yaml +++ b/devices_schedules_restart_handler.yaml @@ -13,7 +13,7 @@ blueprint: ### Required - The Pyscript integration needs to be installed through HACS and properly configured. - - The `scripts/common_utilities.py` script need to be copied into the `config/pyscript` folder. + - The `scripts/common_utilities.py` script needs to be copied into the `config/pyscript` folder. - The `devices_schedules.yaml` blueprint needs to be installed. - The mentioned file(s) is/are included in the repository. - You need to enable two Pyscript options through the UI or via YAML to allow importing all Python packages and to make hass available as a global variable. diff --git a/get_youtube_video_info_full_llm.yaml b/get_youtube_video_info_full_llm.yaml index 96112e9..0ad3167 100644 --- a/get_youtube_video_info_full_llm.yaml +++ b/get_youtube_video_info_full_llm.yaml @@ -9,7 +9,7 @@ blueprint: ### Required - - Check out the guide for further details. + - Check out the guide `home_assistant_play_favorite_youtube_channel_videos_en.md` for further details. - The Feedparser integration needs to be installed from HACS. - Expose YouTube Channel entities after created to Assist. - Consider adding entity aliases to make them easier to remember if needed. diff --git a/home_assistant_device_location_lookup_guide_en.md b/home_assistant_device_location_lookup_guide_en.md index 5f7cfa7..5c72819 100644 --- a/home_assistant_device_location_lookup_guide_en.md +++ b/home_assistant_device_location_lookup_guide_en.md @@ -17,8 +17,8 @@ ## Prerequisites -- **Bermuda BLE Trilateration:** (Optional but recommended) Installed via HACS for room-level tracking. -- **Home Assistant Companion App:** Installed on mobile devices for "Ring to Find" functionality. +- **Bermuda BLE Trilateration:** (Optional but recommended) Installed via HACS for room-level tracking. Make sure your Bluetooth proxies or scanners (ESPHome, Shelly, etc.) are assigned to their corresponding **Areas** in Home Assistant so Bermuda can report the room location. +- **Home Assistant Companion App:** Installed on mobile devices for "Ring to Find" functionality (with notification and critical alert permissions enabled). ## Installation Guide @@ -32,9 +32,9 @@ If your phone has both a Mobile App tracker and a Bermuda tracker: 1. **Expose only the Bermuda tracker** to Voice Assist (it provides better room-level accuracy). -2. **Rename the Bermuda device** to match the Mobile App device name. - - _Example:_ If your Mobile App device is named `Pixel 9`, rename your Bermuda device to `Pixel 9` (or `Pixel 9 BLE`). - - _Why?_ This links the accurate location from Bermuda with the "Ring" capability of the Mobile App. +2. **Ensure the Bermuda device name / entity ID matches or contains the Mobile App device name**: + - _Example:_ If your Mobile App tracker entity is `device_tracker.pixel_9`, naming your Bermuda device `Pixel 9` or `Pixel 9 BLE` results in `device_tracker.pixel_9_ble`. The blueprint automatically identifies that `pixel_9` matches your mobile device. + - _Why?_ This dynamically links the accurate room-level location from Bermuda with the `notify.mobile_app_pixel_9` ringing service of the Mobile App. ### Step 2: Create a Shell Command for Alias Retrieval @@ -72,7 +72,7 @@ template: **After adding these codes:** -1. **Restart** Home Assistant. +1. **Restart** Home Assistant (or reload YAML configurations). 2. **Note:** If you add or change an Alias later, you must reload Template entities (Developer Tools > YAML > Template Entities) or restart HA. ### Step 4: Install Blueprints @@ -86,8 +86,9 @@ This blueprint powers the logic to find where your devices are. 1. Import the blueprint. 2. Create a **Script** from it. 3. In the script settings, select the **Template Sensor** (`sensor.assist_entity_ids_and_aliases`) you created in Step 3. -4. **Keep the default script name** (or ensure it's easy for the LLM to recognize). -5. **Expose** this new script to Voice Assist. +4. **Keep the default script name** (`Find Device`). +5. **Restore LLM Description (Crucial Step):** After saving, click the three dots (`⋮`) in the top-right corner, select **Edit in YAML**, delete the `description: ...` line, and click **Save Script**. This restores the blueprint's native, optimized description so the LLM understands its purpose. +6. **Expose** this new script to Voice Assist. #### 2. Device Ringing Blueprint @@ -97,8 +98,9 @@ This blueprint allows Voice Assist to make your device ring. 1. Import the blueprint. 2. Create a **Script** from it. -3. **Keep the default script name**. -4. **Expose** this new script to Voice Assist. +3. **Keep the default script name** (`Ring Device`). +4. **Restore LLM Description (Crucial Step):** After saving, click the three dots (`⋮`) in the top-right corner, select **Edit in YAML**, delete the `description: ...` line, and click **Save Script**. +5. **Expose** this new script to Voice Assist. ## Usage Examples diff --git a/home_assistant_device_location_lookup_guide_vi.md b/home_assistant_device_location_lookup_guide_vi.md index 23aea98..21125b0 100644 --- a/home_assistant_device_location_lookup_guide_vi.md +++ b/home_assistant_device_location_lookup_guide_vi.md @@ -17,8 +17,8 @@ ## Yêu cầu trước khi cài đặt -- **Bermuda BLE Trilateration:** (Khuyên dùng) Cài đặt qua HACS để có khả năng định vị chính xác theo từng phòng. -- **Home Assistant Companion App:** Cài trên điện thoại/máy tính bảng để sử dụng tính năng đổ chuông. +- **Bermuda BLE Trilateration:** (Khuyên dùng) Cài đặt qua HACS để có khả năng định vị chính xác theo từng phòng. Hãy đảm bảo các Bluetooth proxy hoặc scanner (ESPHome, Shelly...) đã được gán vào đúng các **Khu vực (Areas)** tương ứng trong Home Assistant để Bermuda có thể xác định tên phòng. +- **Home Assistant Companion App:** Cài trên điện thoại/máy tính bảng để sử dụng tính năng đổ chuông (cần cấp quyền thông báo và thông báo khẩn cấp/critical alerts trên iOS). ## Hướng dẫn Cài đặt @@ -32,9 +32,9 @@ Nếu điện thoại của bạn có cả tracker từ Mobile App và Bermuda: 1. **Chỉ expose tracker của Bermuda** cho Voice Assist (để định vị phòng chính xác hơn). -2. **Đổi tên thiết bị Bermuda** trùng với tên thiết bị Mobile App. - - _Ví dụ:_ Nếu Mobile App tên là `Pixel 9`, hãy đổi tên thiết bị Bermuda thành `Pixel 9` (hoặc `Pixel 9 BLE`). - - _Tại sao?_ Việc này giúp liên kết vị trí chính xác từ Bermuda với khả năng "Đổ chuông" của Mobile App. +2. **Đảm bảo tên thiết bị / entity ID của Bermuda trùng hoặc chứa tên thiết bị Mobile App**: + - _Ví dụ:_ Nếu thực thể tracker của Mobile App là `device_tracker.pixel_9`, bạn đặt tên thiết bị Bermuda là `Pixel 9` hoặc `Pixel 9 BLE` (tạo ra `device_tracker.pixel_9_ble`). Blueprint sẽ tự động phát hiện `pixel_9` khớp với thiết bị di động của bạn. + - _Tại sao?_ Việc này giúp liên kết vị trí phòng chính xác từ Bermuda với action đổ chuông `notify.mobile_app_pixel_9` của Mobile App. ### Bước 2: Tạo Shell Command lấy Alias @@ -72,7 +72,7 @@ template: **Sau khi thêm mã:** -1. **Khởi động lại (Restart)** Home Assistant. +1. **Khởi động lại (Restart)** Home Assistant (hoặc reload YAML cấu hình). 2. **Lưu ý:** Nếu sau này bạn thêm hoặc sửa Alias, hãy nhớ reload Template entities (Developer Tools > YAML > Template Entities) hoặc khởi động lại HA. ### Bước 4: Cài đặt Blueprints @@ -86,8 +86,9 @@ Blueprint này xử lý logic để xác định vị trí thiết bị. 1. Import blueprint. 2. Tạo một **Script** từ blueprint này. 3. Trong cấu hình script, chọn **Template Sensor** (`sensor.assist_entity_ids_and_aliases`) đã tạo ở Bước 3. -4. **Giữ nguyên tên script mặc định** (hoặc đặt tên dễ hiểu để LLM nhận diện). -5. **Expose** script này cho Voice Assist. +4. **Giữ nguyên tên script mặc định** (`Find Device`). +5. **Khôi phục mô tả chuẩn cho LLM (Bước cực kỳ quan trọng):** Sau khi lưu script, bấm vào dấu ba chấm (`⋮`) ở góc trên bên phải, chọn **Edit in YAML**, xóa bỏ dòng `description: ...` rồi bấm **Save Script**. Thao tác này giúp script lấy lại phần mô tả gốc của blueprint, giúp LLM hiểu chính xác mục đích sử dụng kịch bản. +6. **Expose** script này cho Voice Assist. #### 2. Blueprint Đổ chuông (Ringing) @@ -97,8 +98,9 @@ Blueprint này cho phép Voice Assist kích hoạt thiết bị đổ chuông. 1. Import blueprint. 2. Tạo một **Script** từ blueprint này. -3. **Giữ nguyên tên script mặc định**. -4. **Expose** script này cho Voice Assist. +3. **Giữ nguyên tên script mặc định** (`Ring Device`). +4. **Khôi phục mô tả chuẩn cho LLM (Bước cực kỳ quan trọng):** Sau khi lưu script, bấm vào dấu ba chấm (`⋮`) ở góc trên bên phải, chọn **Edit in YAML**, xóa bỏ dòng `description: ...` rồi bấm **Save Script**. +5. **Expose** script này cho Voice Assist. ## Ví dụ sử dụng diff --git a/home_assistant_ios_themes_en.md b/home_assistant_ios_themes_en.md index cd60ca9..7cd3dd9 100644 --- a/home_assistant_ios_themes_en.md +++ b/home_assistant_ios_themes_en.md @@ -14,11 +14,8 @@ This guide helps you install a beautiful set of iOS themes, automatically switch ### Install via HACS -1. Go to **HACS** > **Frontend**. -2. Search for and install **iOS Themes** ([basnijholt/lovelace-ios-themes](https://github.com/basnijholt/lovelace-ios-themes)). -3. Go to **HACS** > **Integrations**. -4. Search for and install **Spook** ([frenck/spook](https://github.com/frenck/spook)). - - _Note:_ Spook provides the `input_select.random` feature required for this guide. +1. In Home Assistant, open **HACS**. +2. Search for and download **iOS Themes** ([basnijholt/lovelace-ios-themes](https://github.com/basnijholt/lovelace-ios-themes)). ## 2. Configure Local Backgrounds @@ -102,9 +99,11 @@ actions: - action: input_boolean.turn_{{ 'on' if trigger.event == 'sunset' else 'off' }} target: entity_id: input_boolean.ios_themes_dark_mode - - action: input_select.random + - action: input_select.select_option target: entity_id: input_select.ios_themes + data: + option: "{{ state_attr('input_select.ios_themes', 'options') | random }}" - stop: Settings updated. Waiting for re-trigger to apply theme. - delay: "00:00:01" - action: frontend.set_theme @@ -118,9 +117,11 @@ actions: mode: restart ``` +_Note: If you have Spook installed, you can also use `action: input_select.random` instead of `input_select.select_option`._ + ## 4. Activate Theme on Your Device -**Most Important Step:** For the automation to change your interface, you must select **Use default theme** mode in your user settings. +**Most Important Step:** For the automation to change your interface, you must select **Backend-selected** (or **Use default theme**) mode in your user settings. -1. Click on the **User Profile** icon in the bottom-left corner of the sidebar. -2. Under **Theme**, select **Use default theme**. +1. Click on your **User Profile** icon in the bottom-left corner of the sidebar. +2. Under **Theme**, select **Backend-selected** (or **Use default theme**). diff --git a/home_assistant_ios_themes_vi.md b/home_assistant_ios_themes_vi.md index 70eba26..86786de 100644 --- a/home_assistant_ios_themes_vi.md +++ b/home_assistant_ios_themes_vi.md @@ -14,11 +14,8 @@ Hướng dẫn này giúp bạn cài đặt bộ giao diện iOS đẹp mắt, t ### Cài đặt qua HACS -1. Truy cập **HACS** > **Frontend**. -2. Tìm kiếm và cài đặt **iOS Themes** ([basnijholt/lovelace-ios-themes](https://github.com/basnijholt/lovelace-ios-themes)). -3. Truy cập **HACS** > **Integrations**. -4. Tìm kiếm và cài đặt **Spook** ([frenck/spook](https://github.com/frenck/spook)). - - _Lưu ý:_ Spook cung cấp tính năng `input_select.random` cần thiết cho hướng dẫn này. +1. Trong Home Assistant, mở **HACS**. +2. Tìm kiếm và tải về **iOS Themes** ([basnijholt/lovelace-ios-themes](https://github.com/basnijholt/lovelace-ios-themes)). ## 2. Cấu hình hình nền cục bộ (Local Backgrounds) @@ -102,9 +99,11 @@ actions: - action: input_boolean.turn_{{ 'on' if trigger.event == 'sunset' else 'off' }} target: entity_id: input_boolean.ios_themes_dark_mode - - action: input_select.random + - action: input_select.select_option target: entity_id: input_select.ios_themes + data: + option: "{{ state_attr('input_select.ios_themes', 'options') | random }}" - stop: Settings updated. Waiting for re-trigger to apply theme. - delay: "00:00:01" - action: frontend.set_theme @@ -118,9 +117,11 @@ actions: mode: restart ``` +_Lưu ý: Nếu bạn đã cài đặt Spook, bạn cũng có thể sử dụng `action: input_select.random` thay cho `input_select.select_option`._ + ## 4. Kích hoạt Theme trên thiết bị -**Bước quan trọng nhất:** Để automation có thể thay đổi giao diện của bạn, bạn phải chọn chế độ **Use default theme** trong cài đặt người dùng. +**Bước quan trọng nhất:** Để automation có thể thay đổi giao diện của bạn, bạn phải chọn chế độ **Backend-selected** (hoặc **Use default theme**) trong cài đặt hồ sơ người dùng. 1. Nhấn vào biểu tượng **Hồ sơ người dùng (User Profile)** ở góc dưới cùng bên trái thanh menu. -2. Tại mục **Theme**, chọn **Use default theme**. +2. Tại mục **Theme**, chọn **Backend-selected** (hoặc **Use default theme**). diff --git a/home_assistant_play_favorite_youtube_channel_videos_en.md b/home_assistant_play_favorite_youtube_channel_videos_en.md index 7a47b5b..8245e3f 100644 --- a/home_assistant_play_favorite_youtube_channel_videos_en.md +++ b/home_assistant_play_favorite_youtube_channel_videos_en.md @@ -11,9 +11,10 @@ This guide allows you to use Home Assistant Voice to play the latest videos from ### Limitations -- Does not support searching for old videos from a channel. -- Does not support searching for arbitrary videos across all of YouTube (only searches the latest video of followed channels). -- Requires a supported Smart TV or streaming device integrated into Home Assistant with the official YouTube app installed. +- Focuses on tracking latest video uploads via channel RSS feeds (100% free, no Google Cloud API key or quota required). +- Does not search for older back-catalog videos or arbitrary videos across all of YouTube. + - _Tip:_ If you want to search and play _any_ video across the entire YouTube catalog by keywords, use the companion blueprint **[advanced_youtube_search_full_llm.yaml](README.md#voice-assist---youtube-search--playback)** instead. +- Requires a supported Smart TV or streaming device integrated into Home Assistant (Chromecast, Android TV, Google TV, Apple TV, Roku) with the official YouTube app installed. ![image](images/20250528_210348.jpg) @@ -128,7 +129,7 @@ template: entities: "{{ response.stdout }}" ``` -- After adding, **restart** Home Assistant. +- After adding, **restart** Home Assistant (or reload YAML configurations). - **Note:** Whenever you change an Alias, you must **reload template entities** (from Developer Tools > YAML) or restart HA to update. ## Step 2: Add Scripts for Assist @@ -143,8 +144,9 @@ This blueprint helps Assist fetch the latest video info from the requested YouTu 1. Import the blueprint. 2. Create a new **Script** from this blueprint. 3. Select the Template Sensor (`sensor.assist_entity_ids_and_aliases`) created in step 1.5. - 4. **Important:** Keep the default Script name. - 5. After creating, **Expose** that script to Voice Assist. + 4. **Important:** Keep the default Script name (`Get YouTube Video Info`). + 5. **Restore LLM Description (Crucial Step):** After saving, click the three dots (`⋮`) in the top-right corner, select **Edit in YAML**, delete the `description: ...` line, and click **Save Script**. + 6. After creating, **Expose** that script to Voice Assist. ### 2.2. Install "Play Video" Blueprint @@ -157,8 +159,9 @@ This blueprint is responsible for playing the found video on your media player d 2. Create a new **Script** from this blueprint. 3. Select the Template Sensor (`sensor.assist_entity_ids_and_aliases`) created in step 1.5. 4. Select a **Default** Smart TV to play the video on (Assist will use this if you don't specify a device). - 5. **Important:** Keep the default Script name. - 6. After creating, **Expose** that script to Voice Assist. + 5. **Important:** Keep the default Script name (`Play YouTube Video`). + 6. **Restore LLM Description (Crucial Step):** After saving, click the three dots (`⋮`) in the top-right corner, select **Edit in YAML**, delete the `description: ...` line, and click **Save Script**. + 7. After creating, **Expose** that script to Voice Assist. ## 3. Example Voice Commands diff --git a/home_assistant_play_favorite_youtube_channel_videos_vi.md b/home_assistant_play_favorite_youtube_channel_videos_vi.md index 4d74bd9..a02f8af 100644 --- a/home_assistant_play_favorite_youtube_channel_videos_vi.md +++ b/home_assistant_play_favorite_youtube_channel_videos_vi.md @@ -11,9 +11,10 @@ Hướng dẫn này cho phép bạn sử dụng Home Assistant Voice để phát ### Hạn chế -- Không hỗ trợ tìm kiếm các video cũ từ một kênh. -- Không hỗ trợ tìm kiếm một video bất kỳ trong toàn bộ YouTube (chỉ tìm video mới nhất của kênh đã theo dõi). -- Yêu cầu cần có một Smart TV hoặc thiết bị phát hỗ trợ tích hợp vào Home Assistant và đã cài đặt ứng dụng YouTube chính thức. +- Tập trung vào việc theo dõi các video mới nhất qua RSS feed của kênh (hoàn toàn miễn phí, không tiêu tốn hạn ngạch API Google Cloud). +- Không tìm kiếm các video cũ trong quá khứ hoặc một video bất kỳ trong toàn bộ YouTube. + - _Mẹo:_ Nếu bạn muốn tìm kiếm và phát _bất kỳ_ video nào trên toàn bộ YouTube bằng từ khóa tùy ý, hãy sử dụng blueprint đồng hành **[advanced_youtube_search_full_llm.yaml](README_vi.md#voice-assist---tìm-kiếm--phát-youtube)**. +- Yêu cầu cần có một Smart TV hoặc thiết bị phát hỗ trợ tích hợp vào Home Assistant (Chromecast, Android TV, Google TV, Apple TV, Roku) và đã cài đặt ứng dụng YouTube chính thức. ![image](images/20250528_210348.jpg) @@ -128,7 +129,7 @@ template: entities: "{{ response.stdout }}" ``` -- Sau khi thêm xong, **khởi động lại** Home Assistant. +- Sau khi thêm xong, **khởi động lại** Home Assistant (hoặc reload cấu hình YAML). - **Lưu ý:** Mỗi khi bạn thay đổi Alias, bạn cần **reload template entities** (từ Developer Tools > YAML) hoặc khởi động lại HA để cập nhật. ## Bước 2: Thêm Kịch bản (Script) cho Assist @@ -143,8 +144,9 @@ Blueprint này giúp Assist lấy thông tin video mới nhất từ kênh YouTu 1. Import blueprint. 2. Tạo một **Script** mới từ blueprint này. 3. Chỉ định Template Sensor (`sensor.assist_entity_ids_and_aliases`) đã tạo ở bước 1.5. - 4. **Quan trọng:** Giữ nguyên tên Script mặc định. - 5. Sau khi tạo xong, **Expose** script đó cho Voice Assist. + 4. **Quan trọng:** Giữ nguyên tên Script mặc định (`Get YouTube Video Info`). + 5. **Khôi phục mô tả chuẩn cho LLM (Bước cực kỳ quan trọng):** Sau khi lưu script, bấm vào dấu ba chấm (`⋮`) ở góc trên bên phải, chọn **Edit in YAML**, xóa bỏ dòng `description: ...` rồi bấm **Save Script**. + 6. Sau khi tạo xong, **Expose** script đó cho Voice Assist. ### 2.2. Cài đặt Blueprint Play Video @@ -157,8 +159,9 @@ Blueprint này có nhiệm vụ phát video đã tìm được lên thiết bị 2. Tạo một **Script** mới từ blueprint này. 3. Chỉ định Template Sensor (`sensor.assist_entity_ids_and_aliases`) đã tạo ở bước 1.5. 4. Chọn một Smart TV **Mặc định** để phát video (Assist sẽ dùng thiết bị này nếu bạn không chỉ định thiết bị trong câu lệnh). - 5. **Quan trọng:** Giữ nguyên tên Script mặc định. - 6. Sau khi tạo xong, **Expose** script đó cho Voice Assist. + 5. **Quan trọng:** Giữ nguyên tên Script mặc định (`Play YouTube Video`). + 6. **Khôi phục mô tả chuẩn cho LLM (Bước cực kỳ quan trọng):** Sau khi lưu script, bấm vào dấu ba chấm (`⋮`) ở góc trên bên phải, chọn **Edit in YAML**, xóa bỏ dòng `description: ...` rồi bấm **Save Script**. + 7. Sau khi tạo xong, **Expose** script đó cho Voice Assist. ## 3. Ví dụ lệnh thoại diff --git a/home_assistant_unavailable_devices_en.md b/home_assistant_unavailable_devices_en.md index b90eca7..a1cd136 100644 --- a/home_assistant_unavailable_devices_en.md +++ b/home_assistant_unavailable_devices_en.md @@ -32,14 +32,18 @@ template: unique_id: unavailable_devices device_class: problem icon: >- - {{ iif((this.attributes.raw | default([], true) | length > 0), 'mdi:alert-circle', 'mdi:check-circle') }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ iif((raw_items | length > 0), 'mdi:alert-circle', 'mdi:check-circle') }} state: >- - {{ this.attributes.raw | default([], true) | length > 0 }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items | length > 0 }} attributes: devices: >- - {{ this.attributes.raw | default([], true) | map('device_id') | reject('none') | unique | map('device_attr', 'name') | list }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items | map('device_id') | reject('none') | unique | map('device_attr', 'name') | list }} entities: >- - {{ this.attributes.raw | default([], true) }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items }} raw: >- {% set ignored_domains = ['button', 'event', 'input_button', 'scene', 'stt', 'tts', 'zone'] %} {% set ignored_integrations = ['browser_mod', 'demo', 'group', 'mobile_app', 'private_ble_device'] %} @@ -48,9 +52,10 @@ template: {% for integration in ignored_integrations %} {% set ignored_integration_entities.entities = ignored_integration_entities.entities + integration_entities(integration) %} {% endfor %} + {% set current_entity = this.entity_id if (this is defined and this and this.entity_id is defined) else 'binary_sensor.unavailable_devices' %} {% set candidates = states | selectattr('state', 'eq', 'unavailable') - | rejectattr('entity_id', 'eq', this.entity_id) + | rejectattr('entity_id', 'eq', current_entity) | rejectattr('domain', 'in', ignored_domains) | rejectattr('entity_id', 'in', ignored_integration_entities.entities) | map(attribute='entity_id') @@ -65,7 +70,7 @@ template: {{ ns.final }} ``` -_After saving the file, please **Restart** Home Assistant to apply the changes._ +_After saving the file, please **Restart** Home Assistant (or reload Template Entities) to apply the changes._ ## Step 2: Create Automation Notifications @@ -82,15 +87,13 @@ triggers: - binary_sensor.unavailable_devices attribute: entities conditions: - - condition: template - value_template: "{{ trigger.from_state is not none and trigger.from_state.state not in ['unavailable', 'unknown'] }}" - condition: template value_template: "{{ trigger.to_state is not none and trigger.to_state.state not in ['unavailable', 'unknown'] }}" actions: - variables: entities: "{{ state_attr(trigger.entity_id, 'entities') | default([], true) }}" devices: "{{ state_attr(trigger.entity_id, 'devices') | default([], true) }}" - notify_tag: "{{ 'tag_' ~ this.attributes.id }}" + notify_tag: "{{ 'tag_' ~ (this.attributes.id | default(this.entity_id.split('.')[1] | default('unavailable_devices', true), true)) }}" - if: - condition: template value_template: "{{ entities | length > 0 }}" @@ -128,20 +131,18 @@ triggers: - binary_sensor.unavailable_devices attribute: entities conditions: - - condition: template - value_template: "{{ trigger.from_state is not none and trigger.from_state.state not in ['unavailable', 'unknown'] }}" - condition: template value_template: "{{ trigger.to_state is not none and trigger.to_state.state not in ['unavailable', 'unknown'] }}" actions: - variables: entities: "{{ state_attr(trigger.entity_id, 'entities') | default([], true) }}" devices: "{{ state_attr(trigger.entity_id, 'devices') | default([], true) }}" - notify_tag: "{{ 'tag_' ~ this.attributes.id }}" + notify_tag: "{{ 'tag_' ~ (this.attributes.id | default(this.entity_id.split('.')[1] | default('unavailable_devices_mobile', true), true)) }}" - if: - condition: template value_template: "{{ entities | length > 0 }}" then: - - action: notify.mobile_app_iphone # Replace with your phone's notification service + - action: notify.mobile_app_iphone # Replace with your phone's notification action (e.g. notify.mobile_app_) data: title: "Devices Lost Connection" message: >- @@ -150,7 +151,7 @@ actions: tag: "{{ notify_tag }}" url: /lovelace/system # (Optional) Path to your system dashboard else: - - action: notify.mobile_app_iphone # Replace with your phone's notification service + - action: notify.mobile_app_iphone # Replace with your phone's notification action data: message: clear_notification data: diff --git a/home_assistant_unavailable_devices_vi.md b/home_assistant_unavailable_devices_vi.md index fb953f5..8d32483 100644 --- a/home_assistant_unavailable_devices_vi.md +++ b/home_assistant_unavailable_devices_vi.md @@ -32,14 +32,18 @@ template: unique_id: unavailable_devices device_class: problem icon: >- - {{ iif((this.attributes.raw | default([], true) | length > 0), 'mdi:alert-circle', 'mdi:check-circle') }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ iif((raw_items | length > 0), 'mdi:alert-circle', 'mdi:check-circle') }} state: >- - {{ this.attributes.raw | default([], true) | length > 0 }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items | length > 0 }} attributes: devices: >- - {{ this.attributes.raw | default([], true) | map('device_id') | reject('none') | unique | map('device_attr', 'name') | list }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items | map('device_id') | reject('none') | unique | map('device_attr', 'name') | list }} entities: >- - {{ this.attributes.raw | default([], true) }} + {% set raw_items = (this.attributes.raw if (this is defined and this and this.attributes is defined and 'raw' in this.attributes) else []) %} + {{ raw_items }} raw: >- {% set ignored_domains = ['button', 'event', 'input_button', 'scene', 'stt', 'tts', 'zone'] %} {% set ignored_integrations = ['browser_mod', 'demo', 'group', 'mobile_app', 'private_ble_device'] %} @@ -48,9 +52,10 @@ template: {% for integration in ignored_integrations %} {% set ignored_integration_entities.entities = ignored_integration_entities.entities + integration_entities(integration) %} {% endfor %} + {% set current_entity = this.entity_id if (this is defined and this and this.entity_id is defined) else 'binary_sensor.unavailable_devices' %} {% set candidates = states | selectattr('state', 'eq', 'unavailable') - | rejectattr('entity_id', 'eq', this.entity_id) + | rejectattr('entity_id', 'eq', current_entity) | rejectattr('domain', 'in', ignored_domains) | rejectattr('entity_id', 'in', ignored_integration_entities.entities) | map(attribute='entity_id') @@ -65,7 +70,7 @@ template: {{ ns.final }} ``` -_Sau khi lưu file, hãy **Khởi động lại (Restart)** Home Assistant để áp dụng._ +_Sau khi lưu file, hãy **Khởi động lại (Restart)** Home Assistant (hoặc reload Template Entities) để áp dụng._ ## Bước 2: Tạo Thông báo Tự động (Automation) @@ -82,15 +87,13 @@ triggers: - binary_sensor.unavailable_devices attribute: entities conditions: - - condition: template - value_template: "{{ trigger.from_state is not none and trigger.from_state.state not in ['unavailable', 'unknown'] }}" - condition: template value_template: "{{ trigger.to_state is not none and trigger.to_state.state not in ['unavailable', 'unknown'] }}" actions: - variables: entities: "{{ state_attr(trigger.entity_id, 'entities') | default([], true) }}" devices: "{{ state_attr(trigger.entity_id, 'devices') | default([], true) }}" - notify_tag: "{{ 'tag_' ~ this.attributes.id }}" + notify_tag: "{{ 'tag_' ~ (this.attributes.id | default(this.entity_id.split('.')[1] | default('unavailable_devices', true), true)) }}" - if: - condition: template value_template: "{{ entities | length > 0 }}" @@ -128,20 +131,18 @@ triggers: - binary_sensor.unavailable_devices attribute: entities conditions: - - condition: template - value_template: "{{ trigger.from_state is not none and trigger.from_state.state not in ['unavailable', 'unknown'] }}" - condition: template value_template: "{{ trigger.to_state is not none and trigger.to_state.state not in ['unavailable', 'unknown'] }}" actions: - variables: entities: "{{ state_attr(trigger.entity_id, 'entities') | default([], true) }}" devices: "{{ state_attr(trigger.entity_id, 'devices') | default([], true) }}" - notify_tag: "{{ 'tag_' ~ this.attributes.id }}" + notify_tag: "{{ 'tag_' ~ (this.attributes.id | default(this.entity_id.split('.')[1] | default('unavailable_devices_mobile', true), true)) }}" - if: - condition: template value_template: "{{ entities | length > 0 }}" then: - - action: notify.mobile_app_iphone # Thay bằng tên điện thoại của bạn + - action: notify.mobile_app_iphone # Thay bằng action thông báo điện thoại của bạn (ví dụ notify.mobile_app_) data: title: "Mất kết nối thiết bị" message: >- @@ -150,7 +151,7 @@ actions: tag: "{{ notify_tag }}" url: /lovelace/system # (Tùy chọn) Đường dẫn đến dashboard hệ thống của bạn else: - - action: notify.mobile_app_iphone # Thay bằng tên điện thoại của bạn + - action: notify.mobile_app_iphone # Thay bằng action thông báo điện thoại của bạn data: message: clear_notification data: