Home Assistant Configuration Skill Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization. Slash Commands | Command | Description | |---------|-------------| | | Find duplicate automations and scripts in configuration | /ha-find-duplicates Scans Home Assistant configuration files to find: - Exact duplicates : Automations/scripts with identical triggers and actions - Similar items : Items with 80%+ similarity (name, entities, structure) - Trigger conflicts : Multiple automations responding to the same trigger Usage…

, value):\n entities.add(value)\n elif isinstance(value, list):\n for item in value:\n extract_from_value(item)\n elif isinstance(value, dict):\n for k, v in value.items():\n if k in (\"entity_id\", \"entity\", \"target\", \"service\"):\n if isinstance(v, str):\n entities.add(v)\n elif isinstance(v, list):\n entities.update(v)\n elif isinstance(v, dict) and \"entity_id\" in v:\n eid = v[\"entity_id\"]\n if isinstance(eid, str):\n entities.add(eid)\n elif isinstance(eid, list):\n entities.update(eid)\n else:\n extract_from_value(v)\n\n extract_from_value(config)\n return entities\n\n def _compute_hash(self, item: dict) -> str:\n \"\"\"Compute a hash of the meaningful parts for exact duplicate detection.\"\"\"\n # For automations, hash triggers + actions (ignore alias/id)\n if \"triggers\" in item:\n hashable = {\n \"triggers\": item[\"triggers\"],\n \"conditions\": item[\"conditions\"],\n \"actions\": item[\"actions\"]\n }\n else:\n # For scripts\n hashable = {\n \"sequence\": item[\"sequence\"]\n }\n\n content = json.dumps(hashable, sort_keys=True)\n return hashlib.md5(content.encode()).hexdigest()\n\n def _analyze_duplicates(self) -> dict:\n \"\"\"Analyze collected items for duplicates and similarities.\"\"\"\n result = {\n \"files_scanned\": len(self.files_checked),\n \"automations_found\": len(self.automations),\n \"scripts_found\": len(self.scripts),\n \"exact_duplicates\": {\n \"automations\": [],\n \"scripts\": []\n },\n \"similar_items\": {\n \"automations\": [],\n \"scripts\": []\n },\n \"trigger_conflicts\": [],\n \"errors\": self.errors if self.errors else None\n }\n\n # Find exact duplicates (same hash)\n result[\"exact_duplicates\"][\"automations\"] = self._find_exact_duplicates(self.automations, \"automation\")\n result[\"exact_duplicates\"][\"scripts\"] = self._find_exact_duplicates(self.scripts, \"script\")\n\n # Find similar items\n result[\"similar_items\"][\"automations\"] = self._find_similar_items(self.automations, \"automation\")\n result[\"similar_items\"][\"scripts\"] = self._find_similar_items(self.scripts, \"script\")\n\n # Find trigger conflicts (multiple automations with same trigger)\n result[\"trigger_conflicts\"] = self._find_trigger_conflicts()\n\n # Generate summary\n total_exact = (\n len(result[\"exact_duplicates\"][\"automations\"]) +\n len(result[\"exact_duplicates\"][\"scripts\"])\n )\n total_similar = (\n len(result[\"similar_items\"][\"automations\"]) +\n len(result[\"similar_items\"][\"scripts\"])\n )\n\n if total_exact == 0 and total_similar == 0 and not result[\"trigger_conflicts\"]:\n result[\"status\"] = \"clean\"\n result[\"message\"] = \"No duplicates or conflicts found\"\n else:\n result[\"status\"] = \"duplicates_found\"\n result[\"summary\"] = {\n \"exact_duplicate_groups\": total_exact,\n \"similar_item_groups\": total_similar,\n \"trigger_conflict_groups\": len(result[\"trigger_conflicts\"])\n }\n\n if self.verbose:\n result[\"details\"] = {\n \"files\": self.files_checked,\n \"all_automations\": [\n {\"alias\": a[\"alias\"], \"file\": a[\"file\"], \"id\": a.get(\"id\")}\n for a in self.automations\n ],\n \"all_scripts\": [\n {\"alias\": s[\"alias\"], \"file\": s[\"file\"], \"script_id\": s.get(\"script_id\")}\n for s in self.scripts\n ]\n }\n\n return result\n\n def _find_exact_duplicates(self, items: List[dict], item_type: str) -> List[dict]:\n \"\"\"Find items with identical hashes.\"\"\"\n hash_groups = defaultdict(list)\n\n for item in items:\n hash_groups[item[\"hash\"]].append(item)\n\n duplicates = []\n for hash_val, group in hash_groups.items():\n if len(group) > 1:\n duplicates.append({\n \"type\": item_type,\n \"count\": len(group),\n \"items\": [\n {\n \"alias\": item[\"alias\"],\n \"file\": item[\"file\"],\n \"id\": item.get(\"id\") or item.get(\"script_id\"),\n \"index\": item.get(\"index\")\n }\n for item in group\n ]\n })\n\n return duplicates\n\n def _find_similar_items(self, items: List[dict], item_type: str) -> List[dict]:\n \"\"\"Find items with similar names or overlapping entities.\"\"\"\n similar_groups = []\n checked_pairs = set()\n\n for i, item1 in enumerate(items):\n for j, item2 in enumerate(items):\n if i >= j:\n continue\n\n pair_key = (item1[\"hash\"], item2[\"hash\"])\n if pair_key in checked_pairs:\n continue\n checked_pairs.add(pair_key)\n\n # Skip if they're exact duplicates (already reported)\n if item1[\"hash\"] == item2[\"hash\"]:\n continue\n\n similarity = self._calculate_similarity(item1, item2)\n if similarity >= self.SIMILARITY_THRESHOLD:\n similar_groups.append({\n \"type\": item_type,\n \"similarity\": f\"{similarity:.0%}\",\n \"reason\": self._get_similarity_reason(item1, item2),\n \"items\": [\n {\n \"alias\": item1[\"alias\"],\n \"file\": item1[\"file\"],\n \"id\": item1.get(\"id\") or item1.get(\"script_id\")\n },\n {\n \"alias\": item2[\"alias\"],\n \"file\": item2[\"file\"],\n \"id\": item2.get(\"id\") or item2.get(\"script_id\")\n }\n ]\n })\n\n return similar_groups\n\n def _calculate_similarity(self, item1: dict, item2: dict) -> float:\n \"\"\"Calculate similarity between two items.\"\"\"\n scores = []\n\n # Name similarity\n name_sim = SequenceMatcher(\n None,\n item1[\"alias\"].lower(),\n item2[\"alias\"].lower()\n ).ratio()\n scores.append(name_sim * 0.3) # 30% weight\n\n # Entity overlap\n entities1 = item1.get(\"entities\", set())\n entities2 = item2.get(\"entities\", set())\n if entities1 and entities2:\n overlap = len(entities1 & entities2) / max(len(entities1 | entities2), 1)\n scores.append(overlap * 0.4) # 40% weight\n\n # Trigger similarity (for automations)\n if \"triggers\" in item1 and \"triggers\" in item2:\n trigger_sim = self._compare_structures(item1[\"triggers\"], item2[\"triggers\"])\n scores.append(trigger_sim * 0.3) # 30% weight\n\n # Sequence similarity (for scripts)\n if \"sequence\" in item1 and \"sequence\" in item2:\n seq_sim = self._compare_structures(item1[\"sequence\"], item2[\"sequence\"])\n scores.append(seq_sim * 0.3)\n\n return sum(scores) / max(len(scores) * 0.3, 0.3) # Normalize\n\n def _compare_structures(self, struct1, struct2) -> float:\n \"\"\"Compare two structures for similarity.\"\"\"\n str1 = json.dumps(struct1, sort_keys=True)\n str2 = json.dumps(struct2, sort_keys=True)\n return SequenceMatcher(None, str1, str2).ratio()\n\n def _get_similarity_reason(self, item1: dict, item2: dict) -> str:\n \"\"\"Get a human-readable reason for similarity.\"\"\"\n reasons = []\n\n # Check name similarity\n name_sim = SequenceMatcher(\n None,\n item1[\"alias\"].lower(),\n item2[\"alias\"].lower()\n ).ratio()\n if name_sim > 0.7:\n reasons.append(\"similar names\")\n\n # Check entity overlap\n entities1 = item1.get(\"entities\", set())\n entities2 = item2.get(\"entities\", set())\n if entities1 and entities2:\n common = entities1 & entities2\n if common:\n reasons.append(f\"shared entities: {', '.join(list(common)[:3])}\")\n\n # Check trigger similarity\n if \"triggers\" in item1 and \"triggers\" in item2:\n if self._compare_structures(item1[\"triggers\"], item2[\"triggers\"]) > 0.8:\n reasons.append(\"similar triggers\")\n\n return \"; \".join(reasons) if reasons else \"structural similarity\"\n\n def _find_trigger_conflicts(self) -> List[dict]:\n \"\"\"Find automations with potentially conflicting triggers.\"\"\"\n trigger_groups = defaultdict(list)\n\n for auto in self.automations:\n for trigger in auto.get(\"triggers\", []):\n if isinstance(trigger, dict):\n # Create a simplified trigger key\n trigger_type = trigger.get(\"trigger\") or trigger.get(\"platform\", \"unknown\")\n entity = trigger.get(\"entity_id\", \"\")\n\n if isinstance(entity, list):\n entity = \",\".join(sorted(entity))\n\n key = f\"{trigger_type}:{entity}\"\n if entity: # Only track if there's an entity\n trigger_groups[key].append(auto)\n\n conflicts = []\n for trigger_key, autos in trigger_groups.items():\n if len(autos) > 1:\n conflicts.append({\n \"trigger\": trigger_key,\n \"count\": len(autos),\n \"automations\": [\n {\n \"alias\": a[\"alias\"],\n \"file\": a[\"file\"],\n \"id\": a.get(\"id\")\n }\n for a in autos\n ]\n })\n\n return conflicts\n\n\ndef main():\n if len(sys.argv) \u003c 2:\n print(json.dumps({\n \"error\": \"Usage: find_duplicates.py \u003cdirectory_or_file> [--verbose]\",\n \"examples\": [\n \"python3 find_duplicates.py /config\",\n \"python3 find_duplicates.py automations.yaml --verbose\"\n ]\n }, indent=2))\n sys.exit(1)\n\n path = sys.argv[1]\n verbose = \"--verbose\" in sys.argv or \"-v\" in sys.argv\n\n finder = DuplicateFinder(verbose=verbose)\n result = finder.scan_path(path)\n\n print(json.dumps(result, indent=2, default=str))\n\n # Exit code based on findings\n # Only exit 1 for actual duplicates, not trigger conflicts (often intentional)\n exact_dupes = result.get(\"exact_duplicates\", {})\n similar = result.get(\"similar_items\", {})\n has_real_duplicates = (\n len(exact_dupes.get(\"automations\", [])) > 0 or\n len(exact_dupes.get(\"scripts\", [])) > 0 or\n len(similar.get(\"automations\", [])) > 0 or\n len(similar.get(\"scripts\", [])) > 0\n )\n if has_real_duplicates:\n sys.exit(1)\n sys.exit(0)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":20669,"content_sha256":"99f3a9ea71c502d090448e3b30b8cb8acb2e0ff2991bf6769495b0303b62a796"},{"filename":"scripts/lovelace_validator.py","content":"#!/usr/bin/env python3\n\"\"\"\nHome Assistant Lovelace Dashboard Validator\n\nValidates Lovelace dashboard configurations (YAML and JSON storage format).\nUsage: python3 lovelace_validator.py \u003cfile_path> [--strict]\n\"\"\"\n\nimport sys\nimport re\nimport json\nfrom pathlib import Path\nfrom typing import Dict, List, Set, Any, Optional\n\ntry:\n import yaml\nexcept ImportError:\n print(json.dumps({\n \"valid\": False,\n \"error\": \"PyYAML not installed. Run: pip install pyyaml\"\n }))\n sys.exit(1)\n\n\nclass LovelaceValidator:\n \"\"\"Validator for Lovelace dashboard configurations.\"\"\"\n\n # Built-in card types\n BUILTIN_CARDS = {\n \"alarm-panel\", \"area\", \"button\", \"calendar\", \"conditional\",\n \"entities\", \"entity\", \"entity-filter\", \"gauge\", \"glance\",\n \"grid\", \"heading\", \"history-graph\", \"horizontal-stack\",\n \"humidifier\", \"iframe\", \"light\", \"logbook\", \"map\",\n \"markdown\", \"media-control\", \"picture\", \"picture-elements\",\n \"picture-entity\", \"picture-glance\", \"plant-status\", \"sensor\",\n \"shopping-list\", \"statistic\", \"statistics-graph\", \"thermostat\",\n \"tile\", \"todo-list\", \"vertical-stack\", \"weather-forecast\",\n \"webpage\", \"energy-distribution\", \"energy-usage-graph\"\n }\n\n # Popular custom cards (HACS)\n KNOWN_CUSTOM_CARDS = {\n \"custom:button-card\", \"custom:mushroom-light-card\",\n \"custom:mushroom-entity-card\", \"custom:mushroom-chips-card\",\n \"custom:mushroom-climate-card\", \"custom:mushroom-cover-card\",\n \"custom:mushroom-fan-card\", \"custom:mushroom-person-card\",\n \"custom:mushroom-template-card\", \"custom:mushroom-alarm-control-panel-card\",\n \"custom:mini-graph-card\", \"custom:mini-media-player\",\n \"custom:stack-in-card\", \"custom:layout-card\", \"custom:card-mod\",\n \"custom:auto-entities\", \"custom:swipe-card\", \"custom:fold-entity-row\",\n \"custom:slider-entity-row\", \"custom:bar-card\", \"custom:apexcharts-card\",\n \"custom:vacuum-card\", \"custom:weather-card\", \"custom:clock-weather-card\"\n }\n\n # Valid action types\n VALID_ACTIONS = {\n \"none\", \"more-info\", \"toggle\", \"call-service\", \"navigate\",\n \"url\", \"assist\", \"fire-dom-event\"\n }\n\n def __init__(self, strict: bool = False):\n self.strict = strict\n self.errors: List[dict] = []\n self.warnings: List[dict] = []\n self.cards_found: Dict[str, int] = {}\n self.entities_referenced: Set[str] = set()\n self.custom_cards_used: Set[str] = set()\n\n def validate_file(self, file_path: str) -> dict:\n \"\"\"Validate a Lovelace configuration file.\"\"\"\n path = Path(file_path)\n\n if not path.exists():\n return {\"valid\": False, \"error\": f\"File not found: {file_path}\"}\n\n try:\n content = path.read_text(encoding='utf-8')\n except Exception as e:\n return {\"valid\": False, \"error\": f\"Cannot read file: {e}\"}\n\n # Determine format and parse\n data = None\n file_format = \"unknown\"\n\n # Try JSON first (for .storage/lovelace)\n if path.suffix == '.json' or 'lovelace' in path.name:\n try:\n data = json.loads(content)\n file_format = \"json\"\n # Handle .storage format\n if \"data\" in data and \"config\" in data.get(\"data\", {}):\n data = data[\"data\"][\"config\"]\n except json.JSONDecodeError:\n pass\n\n # Try YAML\n if data is None:\n try:\n data = yaml.safe_load(content)\n file_format = \"yaml\"\n except yaml.YAMLError as e:\n return {\n \"valid\": False,\n \"error\": \"YAML syntax error\",\n \"details\": str(e)\n }\n\n if not data:\n return {\n \"valid\": False,\n \"error\": \"Empty or invalid configuration\"\n }\n\n # Validate structure\n self._validate_dashboard(data)\n\n is_valid = len(self.errors) == 0\n if self.strict:\n is_valid = is_valid and len(self.warnings) == 0\n\n return {\n \"valid\": is_valid,\n \"file\": str(path.absolute()),\n \"format\": file_format,\n \"summary\": {\n \"views\": self._count_views(data),\n \"total_cards\": sum(self.cards_found.values()),\n \"card_types\": self.cards_found,\n \"custom_cards\": list(self.custom_cards_used),\n \"entities_referenced\": len(self.entities_referenced)\n },\n \"errors\": self.errors if self.errors else None,\n \"warnings\": self.warnings if self.warnings else None\n }\n\n def _count_views(self, data: dict) -> int:\n \"\"\"Count views in dashboard.\"\"\"\n if isinstance(data.get(\"views\"), list):\n return len(data[\"views\"])\n return 0\n\n def _validate_dashboard(self, data: dict, path: str = \"\"):\n \"\"\"Validate dashboard structure.\"\"\"\n if not isinstance(data, dict):\n return\n\n # Check for views\n if \"views\" in data:\n views = data[\"views\"]\n if not isinstance(views, list):\n self.errors.append({\n \"path\": \"views\",\n \"message\": \"'views' must be a list\"\n })\n else:\n for i, view in enumerate(views):\n self._validate_view(view, f\"views[{i}]\")\n\n def _validate_view(self, view: dict, path: str):\n \"\"\"Validate a single view.\"\"\"\n if not isinstance(view, dict):\n self.errors.append({\n \"path\": path,\n \"message\": \"View must be an object\"\n })\n return\n\n # Check recommended fields\n if \"title\" not in view and \"path\" not in view:\n self.warnings.append({\n \"path\": path,\n \"message\": \"View should have 'title' or 'path' for navigation\"\n })\n\n # Validate cards\n if \"cards\" in view:\n cards = view[\"cards\"]\n if isinstance(cards, list):\n for i, card in enumerate(cards):\n self._validate_card(card, f\"{path}.cards[{i}]\")\n\n # Validate sections (new dashboard format)\n if \"sections\" in view:\n sections = view[\"sections\"]\n if isinstance(sections, list):\n for i, section in enumerate(sections):\n self._validate_section(section, f\"{path}.sections[{i}]\")\n\n def _validate_section(self, section: dict, path: str):\n \"\"\"Validate a dashboard section.\"\"\"\n if not isinstance(section, dict):\n return\n\n if \"cards\" in section:\n cards = section[\"cards\"]\n if isinstance(cards, list):\n for i, card in enumerate(cards):\n self._validate_card(card, f\"{path}.cards[{i}]\")\n\n def _validate_card(self, card: dict, path: str):\n \"\"\"Validate a single card.\"\"\"\n if not isinstance(card, dict):\n self.errors.append({\n \"path\": path,\n \"message\": \"Card must be an object\"\n })\n return\n\n # Check for type\n card_type = card.get(\"type\")\n if not card_type:\n self.errors.append({\n \"path\": path,\n \"message\": \"Card is missing 'type'\"\n })\n return\n\n # Track card type usage\n self.cards_found[card_type] = self.cards_found.get(card_type, 0) + 1\n\n # Check if it's a custom card\n if card_type.startswith(\"custom:\"):\n self.custom_cards_used.add(card_type)\n if card_type not in self.KNOWN_CUSTOM_CARDS and self.strict:\n self.warnings.append({\n \"path\": path,\n \"message\": f\"Unknown custom card '{card_type}' - ensure it's installed via HACS\"\n })\n elif card_type not in self.BUILTIN_CARDS:\n self.warnings.append({\n \"path\": path,\n \"message\": f\"Unknown card type '{card_type}'\"\n })\n\n # Validate entity references\n self._extract_entities(card, path)\n\n # Validate actions\n for action_key in [\"tap_action\", \"hold_action\", \"double_tap_action\"]:\n if action_key in card:\n self._validate_action(card[action_key], f\"{path}.{action_key}\")\n\n # Validate nested cards (stacks, grids, etc.)\n if card_type in {\"horizontal-stack\", \"vertical-stack\", \"grid\"}:\n if \"cards\" in card:\n for i, nested in enumerate(card.get(\"cards\", [])):\n self._validate_card(nested, f\"{path}.cards[{i}]\")\n\n # Validate conditional cards\n if card_type == \"conditional\":\n if \"card\" in card:\n self._validate_card(card[\"card\"], f\"{path}.card\")\n if \"conditions\" not in card:\n self.errors.append({\n \"path\": path,\n \"message\": \"Conditional card requires 'conditions'\"\n })\n\n def _extract_entities(self, card: dict, path: str):\n \"\"\"Extract and validate entity references.\"\"\"\n # Single entity\n if \"entity\" in card:\n entity = card[\"entity\"]\n if isinstance(entity, str):\n self.entities_referenced.add(entity)\n self._validate_entity_format(entity, f\"{path}.entity\")\n\n # Multiple entities\n if \"entities\" in card:\n entities = card[\"entities\"]\n if isinstance(entities, list):\n for i, entity in enumerate(entities):\n if isinstance(entity, str):\n self.entities_referenced.add(entity)\n self._validate_entity_format(entity, f\"{path}.entities[{i}]\")\n elif isinstance(entity, dict) and \"entity\" in entity:\n self.entities_referenced.add(entity[\"entity\"])\n self._validate_entity_format(entity[\"entity\"], f\"{path}.entities[{i}].entity\")\n\n def _validate_entity_format(self, entity_id: str, path: str):\n \"\"\"Validate entity ID format.\"\"\"\n if not re.match(r'^[a-z_]+\\.[a-z0-9_]+

Home Assistant Configuration Skill Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization. Slash Commands | Command | Description | |---------|-------------| | | Find duplicate automations and scripts in configuration | /ha-find-duplicates Scans Home Assistant configuration files to find: - Exact duplicates : Automations/scripts with identical triggers and actions - Similar items : Items with 80%+ similarity (name, entities, structure) - Trigger conflicts : Multiple automations responding to the same trigger Usage…

, entity_id):\n self.warnings.append({\n \"path\": path,\n \"message\": f\"Entity ID '{entity_id}' may have invalid format (expected: domain.entity_name)\"\n })\n\n def _validate_action(self, action: dict, path: str):\n \"\"\"Validate card action configuration.\"\"\"\n if not isinstance(action, dict):\n return\n\n action_type = action.get(\"action\")\n if action_type and action_type not in self.VALID_ACTIONS:\n self.warnings.append({\n \"path\": path,\n \"message\": f\"Unknown action type '{action_type}'\"\n })\n\n # Validate navigate action\n if action_type == \"navigate\" and \"navigation_path\" not in action:\n self.errors.append({\n \"path\": path,\n \"message\": \"Navigate action requires 'navigation_path'\"\n })\n\n # Validate call-service action\n if action_type == \"call-service\":\n if \"service\" not in action and \"action\" not in action:\n self.errors.append({\n \"path\": path,\n \"message\": \"Call-service action requires 'service' or 'action'\"\n })\n\n # Validate url action\n if action_type == \"url\" and \"url_path\" not in action:\n self.errors.append({\n \"path\": path,\n \"message\": \"URL action requires 'url_path'\"\n })\n\n\ndef main():\n if len(sys.argv) \u003c 2:\n print(json.dumps({\n \"error\": \"Usage: lovelace_validator.py \u003cfile_path> [--strict]\",\n \"examples\": [\n \"python3 lovelace_validator.py ui-lovelace.yaml\",\n \"python3 lovelace_validator.py .storage/lovelace --strict\"\n ]\n }, indent=2))\n sys.exit(1)\n\n file_path = sys.argv[1]\n strict = \"--strict\" in sys.argv\n\n validator = LovelaceValidator(strict=strict)\n result = validator.validate_file(file_path)\n\n print(json.dumps(result, indent=2))\n sys.exit(0 if result.get(\"valid\", False) else 1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":12412,"content_sha256":"3d840096de8c3b9f41cc197096802ff2c17e1d3723ed067b3ab906eba4c71d3a"},{"filename":"scripts/validate_yaml.py","content":"#!/usr/bin/env python3\n\"\"\"\nHome Assistant YAML Validator\n\nValidates YAML files for syntax errors and common Home Assistant issues.\nUsage: python3 validate_yaml.py \u003cfile_path> [--strict]\n\"\"\"\n\nimport sys\nimport re\nimport json\nfrom pathlib import Path\n\ntry:\n import yaml\nexcept ImportError:\n print(json.dumps({\n \"valid\": False,\n \"error\": \"PyYAML not installed. Run: pip install pyyaml\"\n }))\n sys.exit(1)\n\n\nclass HAYamlValidator:\n \"\"\"Validator for Home Assistant YAML configuration files.\"\"\"\n\n # Common issues to check\n DEPRECATED_KEYS = {\n \"service\": \"action\", # 2024+ syntax\n \"trigger\": \"triggers\", # plural form preferred\n \"action\": \"actions\", # plural form in sequences\n }\n\n BOOLEAN_STRINGS = {\"on\", \"off\", \"yes\", \"no\", \"true\", \"false\"}\n\n def __init__(self, strict: bool = False):\n self.strict = strict\n self.errors = []\n self.warnings = []\n\n def validate_file(self, file_path: str) -> dict:\n \"\"\"Validate a YAML file and return results.\"\"\"\n path = Path(file_path)\n\n if not path.exists():\n return {\"valid\": False, \"error\": f\"File not found: {file_path}\"}\n\n if not path.suffix.lower() in ('.yaml', '.yml'):\n self.warnings.append(f\"File extension '{path.suffix}' is not .yaml or .yml\")\n\n try:\n content = path.read_text(encoding='utf-8')\n except Exception as e:\n return {\"valid\": False, \"error\": f\"Cannot read file: {e}\"}\n\n # Check for tabs\n self._check_tabs(content)\n\n # Check for unquoted booleans\n self._check_unquoted_booleans(content)\n\n # Parse YAML\n try:\n data = yaml.safe_load(content)\n except yaml.YAMLError as e:\n error_msg = str(e)\n line_match = re.search(r'line (\\d+)', error_msg)\n line_num = int(line_match.group(1)) if line_match else None\n\n return {\n \"valid\": False,\n \"error\": \"YAML syntax error\",\n \"details\": error_msg,\n \"line\": line_num,\n \"errors\": self.errors,\n \"warnings\": self.warnings\n }\n\n # Validate HA-specific structure\n if data:\n self._validate_ha_structure(data)\n\n is_valid = len(self.errors) == 0\n if self.strict:\n is_valid = is_valid and len(self.warnings) == 0\n\n return {\n \"valid\": is_valid,\n \"file\": str(path.absolute()),\n \"errors\": self.errors,\n \"warnings\": self.warnings,\n \"keys\": list(data.keys()) if isinstance(data, dict) else None\n }\n\n def _check_tabs(self, content: str):\n \"\"\"Check for tab characters in YAML.\"\"\"\n for i, line in enumerate(content.split('\\n'), 1):\n if '\\t' in line:\n self.errors.append(f\"Line {i}: Tab character found (use 2 spaces)\")\n\n def _check_unquoted_booleans(self, content: str):\n \"\"\"Check for unquoted boolean-like values.\"\"\"\n pattern = r':\\s*(on|off|yes|no)\\s*

Home Assistant Configuration Skill Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization. Slash Commands | Command | Description | |---------|-------------| | | Find duplicate automations and scripts in configuration | /ha-find-duplicates Scans Home Assistant configuration files to find: - Exact duplicates : Automations/scripts with identical triggers and actions - Similar items : Items with 80%+ similarity (name, entities, structure) - Trigger conflicts : Multiple automations responding to the same trigger Usage…

\n for i, line in enumerate(content.split('\\n'), 1):\n # Skip comments\n if line.strip().startswith('#'):\n continue\n match = re.search(pattern, line, re.IGNORECASE)\n if match:\n value = match.group(1)\n self.warnings.append(\n f\"Line {i}: Unquoted boolean '{value}' - consider quoting: \\\"{value}\\\"\"\n )\n\n def _validate_ha_structure(self, data: dict, path: str = \"\"):\n \"\"\"Validate Home Assistant specific structure.\"\"\"\n if not isinstance(data, dict):\n return\n\n for key, value in data.items():\n current_path = f\"{path}.{key}\" if path else key\n\n # Check for deprecated keys (warning only)\n if key in self.DEPRECATED_KEYS:\n new_key = self.DEPRECATED_KEYS[key]\n self.warnings.append(\n f\"'{current_path}': Consider using '{new_key}' instead of '{key}' (2024+ syntax)\"\n )\n\n # Validate entity_id patterns\n if key == \"entity_id\" and isinstance(value, str):\n if not re.match(r'^[a-z_]+\\.[a-z0-9_]+

Home Assistant Configuration Skill Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization. Slash Commands | Command | Description | |---------|-------------| | | Find duplicate automations and scripts in configuration | /ha-find-duplicates Scans Home Assistant configuration files to find: - Exact duplicates : Automations/scripts with identical triggers and actions - Similar items : Items with 80%+ similarity (name, entities, structure) - Trigger conflicts : Multiple automations responding to the same trigger Usage…

, value):\n self.warnings.append(\n f\"'{current_path}': Entity ID '{value}' may have invalid format\"\n )\n\n # Recurse into nested structures\n if isinstance(value, dict):\n self._validate_ha_structure(value, current_path)\n elif isinstance(value, list):\n for i, item in enumerate(value):\n if isinstance(item, dict):\n self._validate_ha_structure(item, f\"{current_path}[{i}]\")\n\n\ndef main():\n if len(sys.argv) \u003c 2:\n print(json.dumps({\n \"error\": \"Usage: validate_yaml.py \u003cfile_path> [--strict]\",\n \"example\": \"python3 validate_yaml.py configuration.yaml\"\n }, indent=2))\n sys.exit(1)\n\n file_path = sys.argv[1]\n strict = \"--strict\" in sys.argv\n\n validator = HAYamlValidator(strict=strict)\n result = validator.validate_file(file_path)\n\n print(json.dumps(result, indent=2))\n sys.exit(0 if result.get(\"valid\", False) else 1)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":5340,"content_sha256":"e971bf1b03b5fa6cffc3a31b99f159117ccfc0e33da5906c0bbeecc2d741807b"},{"filename":"skill-report.json","content":"{\n \"schema_version\": \"2.0\",\n \"meta\": {\n \"generated_at\": \"2026-01-17T05:09:52.432Z\",\n \"slug\": \"esjavadex-homeassistant-config\",\n \"source_url\": \"https://github.com/ESJavadex/claude-homeassistant-plugins/tree/main/homeassistant-config/skills/homeassistant-config\",\n \"source_ref\": \"main\",\n \"model\": \"claude\",\n \"analysis_version\": \"3.0.0\",\n \"source_type\": \"community\",\n \"content_hash\": \"b344cdb5802079bad6de1664ed36d3c34cfd6508bba6bf7294c9a1cd4d769216\",\n \"tree_hash\": \"28e545aae238008efa88e406f4653b24acc2cf525db1354c46c6d75241630218\"\n },\n \"skill\": {\n \"name\": \"homeassistant-config\",\n \"description\": \"Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, Lovelace dashboards, and file organization. Use when working with Home Assistant configuration files (.yaml, .yml) or discussing HA automations, scripts, sensors, or dashboards.\",\n \"summary\": \"Create and manage Home Assistant YAML configuration files including automations, scripts, templates,...\",\n \"icon\": \"🏠\",\n \"version\": \"1.0.0\",\n \"author\": \"ESJavadex\",\n \"license\": \"MIT\",\n \"category\": \"data\",\n \"tags\": [\n \"home-assistant\",\n \"automation\",\n \"yaml\",\n \"smart-home\"\n ],\n \"supported_tools\": [\n \"claude\",\n \"codex\",\n \"claude-code\"\n ],\n \"risk_factors\": [\n \"filesystem\"\n ]\n },\n \"security_audit\": {\n \"risk_level\": \"low\",\n \"is_blocked\": false,\n \"safe_to_publish\": true,\n \"summary\": \"The static analyzer produced extensive false positives due to pattern-matching limitations when analyzing Home Assistant YAML configuration documentation. Key findings: 'Weak cryptographic algorithm' (150+ occurrences) incorrectly flags YAML keywords like 'mode: restart' - not actual crypto operations. 'Ruby/shell backtick execution' (448+ occurrences) incorrectly flags markdown code formatting backticks as command execution. 'System reconnaissance' flags documentation of file operations like 'find' and 'grep' commands. The Python validation scripts use standard file operations (read_text, os.walk, yaml.safe_load) for legitimate configuration validation. No malicious code, network calls, credential exfiltration, or actual dangerous patterns detected. This is a legitimate Home Assistant configuration management skill.\",\n \"risk_factor_evidence\": [\n {\n \"factor\": \"filesystem\",\n \"evidence\": [\n {\n \"file\": \"scripts/lovelace_validator.py\",\n \"line_start\": 77,\n \"line_end\": 79\n },\n {\n \"file\": \"scripts/validate_yaml.py\",\n \"line_start\": 51,\n \"line_end\": 54\n },\n {\n \"file\": \"scripts/find_duplicates.py\",\n \"line_start\": 101,\n \"line_end\": 103\n },\n {\n \"file\": \"scripts/check_config.py\",\n \"line_start\": 87,\n \"line_end\": 89\n }\n ]\n }\n ],\n \"critical_findings\": [],\n \"high_findings\": [],\n \"medium_findings\": [],\n \"low_findings\": [],\n \"dangerous_patterns\": [],\n \"files_scanned\": 19,\n \"total_lines\": 7880,\n \"audit_model\": \"claude\",\n \"audited_at\": \"2026-01-17T05:09:52.432Z\"\n },\n \"content\": {\n \"user_title\": \"Create Home Assistant automations and dashboards\",\n \"value_statement\": \"Home Assistant configuration files require precise YAML syntax and proper structure. This skill provides validation tools, example configurations, and best practices for automations, scripts, templates, blueprints, and Lovelace dashboards.\",\n \"seo_keywords\": [\n \"Home Assistant configuration\",\n \"Claude automation skill\",\n \"Home Assistant YAML validation\",\n \"Claude Code smart home\",\n \"Home Assistant automations\",\n \"Lovelace dashboard configuration\",\n \"Home Assistant blueprints\",\n \"Jinja2 templates\",\n \"Claude home automation\",\n \"Codex YAML configuration\"\n ],\n \"actual_capabilities\": [\n \"Validate YAML syntax and Home Assistant structure\",\n \"Find duplicate automations and scripts\",\n \"Create automations with modern 2024+ syntax\",\n \"Build Lovelace dashboards with cards and views\",\n \"Write Jinja2 template sensors\",\n \"Create reusable blueprints with inputs\"\n ],\n \"limitations\": [\n \"Does not execute or deploy configurations to Home Assistant\",\n \"Does not access Home Assistant API or entity states\",\n \"Does not validate runtime integration compatibility\",\n \"Scripts require Python and PyYAML installed\"\n ],\n \"use_cases\": [\n {\n \"target_user\": \"Home automation beginners\",\n \"title\": \"Create first automation\",\n \"description\": \"Set up motion-activated lights with proper YAML structure and syntax\"\n },\n {\n \"target_user\": \"Experienced users\",\n \"title\": \"Build complex dashboards\",\n \"description\": \"Design Lovelace dashboards with custom cards, conditional displays, and responsive layouts\"\n },\n {\n \"target_user\": \"Power users\",\n \"title\": \"Optimize configuration\",\n \"description\": \"Find duplicate automations, refactor scripts, and implement blueprints for reusability\"\n }\n ],\n \"prompt_templates\": [\n {\n \"title\": \"Basic automation\",\n \"scenario\": \"Create a motion light automation\",\n \"prompt\": \"Create an automation that turns on the living room light when motion is detected after sunset, with a 5-minute delay before turning off\"\n },\n {\n \"title\": \"Template sensor\",\n \"scenario\": \"Calculate energy usage\",\n \"prompt\": \"Write a template sensor that calculates the total power usage from multiple smart plugs and shows daily energy cost\"\n },\n {\n \"title\": \"Lovelace dashboard\",\n \"scenario\": \"Build a room card\",\n \"prompt\": \"Create a Mushroom cards configuration for the living room that shows temperature, humidity, and light controls in a compact layout\"\n },\n {\n \"title\": \"Blueprint creation\",\n \"scenario\": \"Create reusable template\",\n \"prompt\": \"Create a blueprint for motion-activated lighting with configurable motion sensor, target light, wait time, and day/night brightness settings\"\n }\n ],\n \"output_examples\": [\n {\n \"input\": \"Create an automation that turns on hallway lights when motion is detected between sunset and sunrise\",\n \"output\": [\n \"automation:\",\n \" - alias: 'Hallway - Motion Lights'\",\n \" id: hallway_motion_lights\",\n \" description: 'Turn on hallway lights when motion is detected at night'\",\n \" mode: restart\",\n \" triggers:\",\n \" - trigger: state\",\n \" entity_id: binary_sensor.hallway_motion\",\n \" to: 'on'\",\n \" conditions:\",\n \" - condition: sun\",\n \" after: sunset\",\n \" before: sunrise\",\n \" actions:\",\n \" - action: light.turn_on\",\n \" target:\",\n \" entity_id: light.hallway\",\n \" data:\",\n \" brightness_pct: 50\"\n ]\n },\n {\n \"input\": \"Write a template sensor that combines temperature and humidity into a comfort score\",\n \"output\": [\n \"template:\",\n \" - sensor:\",\n \" - name: 'Room Comfort Score'\",\n \" unit_of_measurement: '%'\",\n \" state: >\",\n \" {% set temp = states('sensor.temperature') | float(20) %}\",\n \" {% set humidity = states('sensor.humidity') | float(50) %}\",\n \" {% set temp_score = 100 - (temp - 22) | abs * 4 %}\",\n \" {% set humidity_score = 100 - (humidity - 45) | abs * 2 %}\",\n \" {{ (temp_score + humidity_score) / 2 | round(0) }}\"\n ]\n }\n ],\n \"best_practices\": [\n \"Always use 2 spaces for indentation, never tabs\",\n \"Quote boolean-like values: 'on', 'off', 'yes', 'no'\",\n \"Use plural form for triggers, conditions, and actions\",\n \"Add unique IDs to automations for UI editing support\"\n ],\n \"anti_patterns\": [\n \"Using tabs instead of spaces in YAML files\",\n \"Unquoted strings that look like booleans\",\n \"Missing mode specification in automations\",\n \"Hardcoding entity IDs instead of using variables\"\n ],\n \"faq\": [\n {\n \"question\": \"What versions of Home Assistant are supported?\",\n \"answer\": \"This skill uses 2024+ syntax with modern trigger, condition, and action formats. Most patterns work with 2022.4+.\"\n },\n {\n \"question\": \"What are the validation script requirements?\",\n \"answer\": \"Scripts require Python 3 and PyYAML installed. Run pip install pyyaml before using validation tools.\"\n },\n {\n \"question\": \"Can I integrate with existing Home Assistant?\",\n \"answer\": \"This skill generates configuration files. You must manually copy or deploy files to your Home Assistant instance.\"\n },\n {\n \"question\": \"Is my data safe with this skill?\",\n \"answer\": \"Yes. Scripts only read files for validation. No data is sent externally or stored beyond the session.\"\n },\n {\n \"question\": \"Why does the skill need filesystem access?\",\n \"answer\": \"File access is required to validate YAML syntax, check for duplicates, and analyze configuration structure.\"\n },\n {\n \"question\": \"How does this compare to Home Assistant Studio?\",\n \"answer\": \"This skill provides Claude Code integration for generating and validating configurations. Studio is a web-based editor.\"\n }\n ]\n },\n \"file_structure\": [\n {\n \"name\": \"examples\",\n \"type\": \"dir\",\n \"path\": \"examples\",\n \"children\": [\n {\n \"name\": \"automations.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/automations.yaml\",\n \"lines\": 368\n },\n {\n \"name\": \"configuration.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/configuration.yaml\",\n \"lines\": 73\n },\n {\n \"name\": \"lovelace-custom-cards.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/lovelace-custom-cards.yaml\",\n \"lines\": 559\n },\n {\n \"name\": \"lovelace-dashboard.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/lovelace-dashboard.yaml\",\n \"lines\": 466\n },\n {\n \"name\": \"scripts.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/scripts.yaml\",\n \"lines\": 363\n },\n {\n \"name\": \"secrets.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/secrets.yaml\",\n \"lines\": 32\n },\n {\n \"name\": \"templates.yaml\",\n \"type\": \"file\",\n \"path\": \"examples/templates.yaml\",\n \"lines\": 204\n }\n ]\n },\n {\n \"name\": \"references\",\n \"type\": \"dir\",\n \"path\": \"references\",\n \"children\": [\n {\n \"name\": \"best-practices.md\",\n \"type\": \"file\",\n \"path\": \"references/best-practices.md\",\n \"lines\": 324\n },\n {\n \"name\": \"blueprints.md\",\n \"type\": \"file\",\n \"path\": \"references/blueprints.md\",\n \"lines\": 615\n },\n {\n \"name\": \"lovelace.md\",\n \"type\": \"file\",\n \"path\": \"references/lovelace.md\",\n \"lines\": 812\n },\n {\n \"name\": \"patterns.md\",\n \"type\": \"file\",\n \"path\": \"references/patterns.md\",\n \"lines\": 822\n },\n {\n \"name\": \"templates.md\",\n \"type\": \"file\",\n \"path\": \"references/templates.md\",\n \"lines\": 596\n },\n {\n \"name\": \"troubleshooting.md\",\n \"type\": \"file\",\n \"path\": \"references/troubleshooting.md\",\n \"lines\": 294\n }\n ]\n },\n {\n \"name\": \"scripts\",\n \"type\": \"dir\",\n \"path\": \"scripts\",\n \"children\": [\n {\n \"name\": \"check_config.py\",\n \"type\": \"file\",\n \"path\": \"scripts/check_config.py\",\n \"lines\": 282\n },\n {\n \"name\": \"find_duplicates.py\",\n \"type\": \"file\",\n \"path\": \"scripts/find_duplicates.py\",\n \"lines\": 558\n },\n {\n \"name\": \"lovelace_validator.py\",\n \"type\": \"file\",\n \"path\": \"scripts/lovelace_validator.py\",\n \"lines\": 346\n },\n {\n \"name\": \"validate_yaml.py\",\n \"type\": \"file\",\n \"path\": \"scripts/validate_yaml.py\",\n \"lines\": 166\n }\n ]\n },\n {\n \"name\": \"SKILL.md\",\n \"type\": \"file\",\n \"path\": \"SKILL.md\",\n \"lines\": 673\n }\n ]\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":12890,"content_sha256":"28ac13c4cfcffb8c84c43bd6a9286bf068150b842ef242eb2e655c30ad8c30f8"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Home Assistant Configuration Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Slash Commands","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Command","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/ha-find-duplicates [path]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Find duplicate automations and scripts in configuration","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"/ha-find-duplicates","type":"text"}]},{"type":"paragraph","content":[{"text":"Scans Home Assistant configuration files to find:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exact duplicates","type":"text","marks":[{"type":"strong"}]},{"text":": Automations/scripts with identical triggers and actions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Similar items","type":"text","marks":[{"type":"strong"}]},{"text":": Items with 80%+ similarity (name, entities, structure)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trigger conflicts","type":"text","marks":[{"type":"strong"}]},{"text":": Multiple automations responding to the same trigger","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Usage: ","type":"text"},{"text":"/ha-find-duplicates /path/to/config","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"/ha-find-duplicates","type":"text","marks":[{"type":"code_inline"}]},{"text":" for current directory.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Subagents","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Agent","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ha-suggestions","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart home improvement advisor for automations, scenes, and device recommendations","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"ha-suggestions","type":"text"}]},{"type":"paragraph","content":[{"text":"A proactive smart home consultant that analyzes your Home Assistant configuration and provides personalized suggestions for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"New Automations","type":"text","marks":[{"type":"strong"}]},{"text":": Motion lighting, presence detection, time-based routines, energy saving","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"New Scenes","type":"text","marks":[{"type":"strong"}]},{"text":": Movie night, morning energy, dinner time, work from home, party mode","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script Improvements","type":"text","marks":[{"type":"strong"}]},{"text":": Reusable sequences and parameterized routines","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Device Recommendations","type":"text","marks":[{"type":"strong"}]},{"text":": Sensors, switches, and integrations to enhance your setup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optimization","type":"text","marks":[{"type":"strong"}]},{"text":": Consolidation, trigger efficiency, mode usage, blueprint conversion","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The agent automatically discovers your configuration files, inventories entities by domain (lights, sensors, climate, etc.), and generates prioritized suggestions with complete, ready-to-use YAML code.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation Scripts","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill includes scripts to validate and analyze Home Assistant configurations.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"YAML Validator","type":"text"}]},{"type":"paragraph","content":[{"text":"Validates YAML syntax and checks for common HA issues (tabs, unquoted booleans, deprecated syntax):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 {baseDir}/scripts/validate_yaml.py /path/to/config.yaml\npython3 {baseDir}/scripts/validate_yaml.py /path/to/config.yaml --strict","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Configuration Checker","type":"text"}]},{"type":"paragraph","content":[{"text":"Analyzes HA configuration structure, finds entities, tracks includes and secrets:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 {baseDir}/scripts/check_config.py /path/to/config/directory\npython3 {baseDir}/scripts/check_config.py /path/to/config.yaml --verbose","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Lovelace Validator","type":"text"}]},{"type":"paragraph","content":[{"text":"Validates Lovelace dashboard configurations (YAML and JSON .storage format):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 {baseDir}/scripts/lovelace_validator.py /path/to/ui-lovelace.yaml\npython3 {baseDir}/scripts/lovelace_validator.py /path/to/.storage/lovelace --strict","type":"text"}]},{"type":"paragraph","content":[{"text":"Features:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validates card types (built-in and custom)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Checks entity ID formats","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validates actions (tap_action, hold_action)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detects custom cards (HACS)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Supports both YAML and JSON storage formats","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Duplicate Finder","type":"text"}]},{"type":"paragraph","content":[{"text":"Finds duplicate and similar automations/scripts across configuration files:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 {baseDir}/scripts/find_duplicates.py /path/to/config/directory\npython3 {baseDir}/scripts/find_duplicates.py /path/to/automations.yaml --verbose","type":"text"}]},{"type":"paragraph","content":[{"text":"Features:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exact duplicate detection (identical triggers + actions)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Similar item detection (80% threshold for names, entities, structure)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trigger conflict detection (multiple automations on same trigger)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Entity overlap analysis between automations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"JSON output with detailed findings","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Pre-Save Validation Hook","type":"text"}]},{"type":"paragraph","content":[{"text":"This plugin includes a pre-save hook that automatically validates YAML files before saving. It checks for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tab characters (HA requires spaces)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Basic YAML syntax errors","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The hook runs automatically on Write/Edit operations for ","type":"text"},{"text":".yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":".yml","type":"text","marks":[{"type":"code_inline"}]},{"text":" files.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"YAML Requirements","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Indentation","type":"text","marks":[{"type":"strong"}]},{"text":": 2 spaces per level (never tabs)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Strings","type":"text","marks":[{"type":"strong"}]},{"text":": Quote boolean-like values (\"on\", \"off\", \"yes\", \"no\")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Lists","type":"text","marks":[{"type":"strong"}]},{"text":": Use ","type":"text"},{"text":"-","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix with proper indentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Comments","type":"text","marks":[{"type":"strong"}]},{"text":": Use ","type":"text"},{"text":"#","type":"text","marks":[{"type":"code_inline"}]},{"text":" for inline documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Key Terms","type":"text","marks":[{"type":"strong"}]},{"text":": Use ","type":"text"},{"text":"action:","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"service:","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":"triggers:","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"trigger:","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":"actions:","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not ","type":"text"},{"text":"action:","type":"text","marks":[{"type":"code_inline"}]},{"text":" for sequences)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"File Organization","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Includes","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# configuration.yaml\nautomation: !include automations.yaml\nscript: !include scripts.yaml\nsensor: !include sensors.yaml","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Directory Includes","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Merge all files in directory\nautomation: !include_dir_merge_list automations/\nsensor: !include_dir_merge_list sensors/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Secrets Management","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# secrets.yaml\nmqtt_password: \"super_secret_password\"\napi_key: \"your-api-key-here\"\n\n# configuration.yaml\nmqtt:\n password: !secret mqtt_password","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Automations (2024+ Syntax)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"automation:\n - alias: \"Descriptive Name\"\n id: unique_automation_id\n description: \"What this automation does\"\n mode: single # single, restart, queued, parallel\n triggers:\n - trigger: state\n entity_id: binary_sensor.motion\n to: \"on\"\n conditions:\n - condition: time\n after: \"sunset\"\n actions:\n - action: light.turn_on\n target:\n entity_id: light.living_room","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Triggers","type":"text"}]},{"type":"paragraph","content":[{"text":"State Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: state\n entity_id: sensor.temperature\n from: \"off\"\n to: \"on\"\n for:\n minutes: 5","type":"text"}]},{"type":"paragraph","content":[{"text":"Time Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: time\n at: \"07:00:00\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Numeric State Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: numeric_state\n entity_id: sensor.temperature\n above: 25\n below: 30","type":"text"}]},{"type":"paragraph","content":[{"text":"Sun Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: sun\n event: sunset\n offset: \"-00:30:00\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Template Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: template\n value_template: \"{{ states('sensor.power') | float > 1000 }}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Calendar Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: calendar\n entity_id: calendar.work\n event: start\n offset: \"-00:15:00\" # 15 min before event","type":"text"}]},{"type":"paragraph","content":[{"text":"Device Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: device\n device_id: abc123\n domain: zwave_js\n type: event.value_notification.entry_control","type":"text"}]},{"type":"paragraph","content":[{"text":"Event Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: event\n event_type: mobile_app_notification_action\n event_data:\n action: \"CONFIRM_ACTION\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Trigger IDs (for multi-trigger automations)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"triggers:\n - trigger: state\n id: \"motion_detected\"\n entity_id: binary_sensor.motion\n to: \"on\"\n - trigger: state\n id: \"door_opened\"\n entity_id: binary_sensor.door\n to: \"on\"\nactions:\n - choose:\n - conditions:\n - condition: trigger\n id: \"motion_detected\"\n sequence:\n - action: light.turn_on\n target:\n entity_id: light.hallway","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Actions","type":"text"}]},{"type":"paragraph","content":[{"text":"Service Call","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - action: light.turn_on\n target:\n entity_id: light.bedroom\n data:\n brightness_pct: 50\n color_temp: 350","type":"text"}]},{"type":"paragraph","content":[{"text":"Delay","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - delay:\n seconds: 30","type":"text"}]},{"type":"paragraph","content":[{"text":"Conditional (Choose)","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - choose:\n - conditions:\n - condition: state\n entity_id: sun.sun\n state: \"below_horizon\"\n sequence:\n - action: light.turn_on\n target:\n entity_id: light.porch\n default:\n - action: light.turn_off\n target:\n entity_id: light.porch","type":"text"}]},{"type":"paragraph","content":[{"text":"Repeat","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - repeat:\n count: 3\n sequence:\n - action: notify.mobile_app\n data:\n message: \"Alert!\"\n - delay:\n minutes: 1","type":"text"}]},{"type":"paragraph","content":[{"text":"If-Then-Else","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - if:\n - condition: state\n entity_id: sun.sun\n state: \"below_horizon\"\n then:\n - action: light.turn_on\n target:\n entity_id: light.porch\n else:\n - action: light.turn_off\n target:\n entity_id: light.porch","type":"text"}]},{"type":"paragraph","content":[{"text":"Parallel Actions","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - parallel:\n - action: notify.person1\n data:\n message: \"Alert sent simultaneously!\"\n - action: notify.person2\n data:\n message: \"Alert sent simultaneously!\"\n - sequence:\n - action: light.turn_on\n target:\n entity_id: light.alarm\n - delay:\n seconds: 5\n - action: light.turn_off\n target:\n entity_id: light.alarm","type":"text"}]},{"type":"paragraph","content":[{"text":"Wait for Trigger","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - action: light.turn_on\n target:\n entity_id: light.porch\n - wait_for_trigger:\n - trigger: state\n entity_id: binary_sensor.motion\n to: \"off\"\n timeout:\n minutes: 10\n continue_on_timeout: true\n - action: light.turn_off\n target:\n entity_id: light.porch","type":"text"}]},{"type":"paragraph","content":[{"text":"Response Variables (get data from actions)","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - action: calendar.get_events\n target:\n entity_id: calendar.work\n data:\n duration:\n hours: 24\n response_variable: agenda\n - action: notify.mobile_app\n data:\n message: \"You have {{ agenda['calendar.work'].events | count }} events today\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Continue on Error","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - action: notify.unreliable_service\n data:\n message: \"This might fail\"\n continue_on_error: true\n - action: light.turn_on\n target:\n entity_id: light.bedroom","type":"text"}]},{"type":"paragraph","content":[{"text":"Stop with Response","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"actions:\n - if:\n - condition: state\n entity_id: input_boolean.enabled\n state: \"off\"\n then:\n - stop: \"Feature is disabled\"\n error: true\n - action: script.do_something","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scripts","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"script:\n morning_routine:\n alias: \"Morning Routine\"\n description: \"Start the day\"\n fields:\n brightness:\n description: \"Light brightness\"\n example: 80\n default: 100\n selector:\n number:\n min: 0\n max: 100\n sequence:\n - action: light.turn_on\n target:\n area_id: bedroom\n data:\n brightness_pct: \"{{ brightness }}\"\n - action: media_player.play_media\n target:\n entity_id: media_player.speaker\n data:\n media_content_id: \"morning_news\"\n media_content_type: \"music\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Jinja2 Templates","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"State Functions","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Get state\n{{ states('sensor.temperature') }}\n\n# Get attribute\n{{ state_attr('climate.thermostat', 'current_temperature') }}\n\n# Check if entity exists\n{{ states.sensor.temperature is defined }}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Filters and Tests","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# Convert types\n{{ states('sensor.temp') | float(0) }}\n{{ states('sensor.count') | int(0) }}\n\n# Math operations\n{{ states('sensor.power') | float * 0.15 | round(2) }}\n\n# Date/time\n{{ now().strftime('%H:%M') }}\n{{ as_timestamp(now()) }}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Template Sensors","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"template:\n - sensor:\n - name: \"Total Power Usage\"\n unit_of_measurement: \"W\"\n state: >\n {{ states('sensor.plug_1_power') | float(0) +\n states('sensor.plug_2_power') | float(0) }}\n availability: >\n {{ states('sensor.plug_1_power') not in ['unknown', 'unavailable'] }}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Lovelace Dashboards","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Enable YAML Mode","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# configuration.yaml\nlovelace:\n mode: yaml","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Dashboard Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# ui-lovelace.yaml\ntitle: My Home\nviews:\n - title: Home\n path: home\n icon: mdi:home\n cards:\n - type: entities\n title: Living Room\n entities:\n - light.living_room\n - switch.fan","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common Card Types","type":"text"}]},{"type":"paragraph","content":[{"text":"Entities Card","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: entities\ntitle: Room Controls\nstate_color: true\nentities:\n - entity: light.ceiling\n name: Ceiling Light\n - type: divider\n - entity: climate.thermostat","type":"text"}]},{"type":"paragraph","content":[{"text":"Button Card","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: button\nentity: light.bedroom\nname: Bedroom\nicon: mdi:lightbulb\ntap_action:\n action: toggle\nhold_action:\n action: more-info","type":"text"}]},{"type":"paragraph","content":[{"text":"Grid Layout","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: grid\ncolumns: 3\nsquare: true\ncards:\n - type: button\n entity: light.1\n - type: button\n entity: light.2\n - type: button\n entity: light.3","type":"text"}]},{"type":"paragraph","content":[{"text":"Area Card","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: area\narea: living_room\ndisplay_type: compact\nnavigation_path: /lovelace/living-room\nsensor_classes:\n - temperature\n - humidity","type":"text"}]},{"type":"paragraph","content":[{"text":"Conditional Card","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: conditional\nconditions:\n - condition: state\n entity: person.john\n state: home\ncard:\n type: entities\n entities:\n - light.johns_room","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Card Actions","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"tap_action:\n action: toggle # Toggle entity\n # action: more-info # Show details\n # action: navigate # Go to view\n # navigation_path: /lovelace/lights\n # action: call-service # Call action\n # service: light.turn_on\n # target:\n # entity_id: light.all","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Popular Custom Cards (via HACS)","type":"text"}]},{"type":"paragraph","content":[{"text":"Mushroom Cards","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: custom:mushroom-light-card\nentity: light.bedroom\nshow_brightness_control: true\nuse_light_color: true","type":"text"}]},{"type":"paragraph","content":[{"text":"Button Card (Custom)","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"type: custom:button-card\nentity: light.bedroom\nname: Bedroom\nstyles:\n card:\n - border-radius: 12px\nstate:\n - value: \"on\"\n styles:\n icon:\n - color: amber","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation Tools","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Developer Tools > YAML","type":"text","marks":[{"type":"strong"}]},{"text":": Check configuration syntax","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Developer Tools > Template","type":"text","marks":[{"type":"strong"}]},{"text":": Test Jinja2 templates","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Developer Tools > States","type":"text","marks":[{"type":"strong"}]},{"text":": Verify entity states","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Logs","type":"text","marks":[{"type":"strong"}]},{"text":": Enable debug logging for troubleshooting","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"logger:\n default: info\n logs:\n homeassistant.components.automation: debug","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Issues","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Problem","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tab characters","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Replace with 2 spaces","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unquoted booleans","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quote \"on\", \"off\", \"yes\", \"no\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Template errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test in Developer Tools first","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Entity not found","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check entity_id spelling","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Automation not firing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Verify trigger conditions in trace","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Blueprints","type":"text"}]},{"type":"paragraph","content":[{"text":"Reusable automation templates with configurable inputs:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"blueprint:\n name: Motion-activated Light\n description: Turn on a light when motion is detected\n domain: automation\n input:\n motion_sensor:\n name: Motion Sensor\n selector:\n entity:\n filter:\n - domain: binary_sensor\n device_class: motion\n target_light:\n name: Light\n selector:\n target:\n entity:\n - domain: light\n delay_time:\n name: Delay\n default: 120\n selector:\n number:\n min: 0\n max: 3600\n unit_of_measurement: seconds\n\ntriggers:\n - trigger: state\n entity_id: !input motion_sensor\n to: \"on\"\n\nactions:\n - action: light.turn_on\n target: !input target_light\n - wait_for_trigger:\n - trigger: state\n entity_id: !input motion_sensor\n to: \"off\"\n for:\n seconds: !input delay_time\n - action: light.turn_off\n target: !input target_light","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Files","type":"text"}]},{"type":"paragraph","content":[{"text":"For detailed patterns and examples, see:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Common automation patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/templates.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Template sensor examples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/lovelace.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Dashboard cards and layouts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Error solutions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/best-practices.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Optimization tips","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/blueprints.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Blueprint creation guide","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"examples/","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Complete working configurations","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"homeassistant-config","author":"@skillopedia","source":{"stars":336,"repo_name":"marketplace","origin_url":"https://github.com/aiskillstore/marketplace/blob/HEAD/skills/esjavadex/homeassistant-config/SKILL.md","repo_owner":"aiskillstore","body_sha256":"96539b918b5215ec250c6895a220291a071f8d23426d82359b8a010e1e0af110","cluster_key":"4a4ccc902318507ba9cf7f1c187996aad3c6fa4eeff723ee64f3140c1278e030","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aiskillstore/marketplace/skills/esjavadex/homeassistant-config/SKILL.md","attachments":[{"id":"3a01da0f-3f41-5942-a439-4ea875cd5920","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3a01da0f-3f41-5942-a439-4ea875cd5920/attachment.yaml","path":"examples/automations.yaml","size":9492,"sha256":"1a0b019123031ccb0d6bec713142842e1221d385f77c8f16994c8b5c4e0f7488","contentType":"application/yaml; charset=utf-8"},{"id":"15d2928a-09a6-569d-be00-b831291ef9b9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/15d2928a-09a6-569d-be00-b831291ef9b9/attachment.yaml","path":"examples/configuration.yaml","size":1159,"sha256":"d395ec3bec0bd4e88fa6e4aa07621825fc53721c35bed2f394cb06847cf672bb","contentType":"application/yaml; charset=utf-8"},{"id":"d8b50881-a01b-5274-8ea9-1362b9a81987","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d8b50881-a01b-5274-8ea9-1362b9a81987/attachment.yaml","path":"examples/lovelace-custom-cards.yaml","size":13410,"sha256":"c0c5e6d7f0379643625f9d59c207f0cc844c7843a9151d4c1819c960839105b9","contentType":"application/yaml; charset=utf-8"},{"id":"39c6eaf8-b681-5dba-8676-32638203bd94","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/39c6eaf8-b681-5dba-8676-32638203bd94/attachment.yaml","path":"examples/lovelace-dashboard.yaml","size":12323,"sha256":"569fe2a8012ba209caa924e0b5e10e810959b1710f70523bb73dd37bd8a97d95","contentType":"application/yaml; charset=utf-8"},{"id":"cd3329b0-37e0-5733-a1f8-5c7f1adcedac","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cd3329b0-37e0-5733-a1f8-5c7f1adcedac/attachment.yaml","path":"examples/scripts.yaml","size":9673,"sha256":"8fe33a088808fd46f088f16689652af1825003c8f9f75e522f80a157a62258a6","contentType":"application/yaml; charset=utf-8"},{"id":"09607691-1a5e-5924-be48-acf4102389e8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/09607691-1a5e-5924-be48-acf4102389e8/attachment.yaml","path":"examples/secrets.yaml","size":822,"sha256":"33dc82be4dd9360946048f9dea07903a133020a3ef038a120dca82565816935d","contentType":"application/yaml; charset=utf-8"},{"id":"c70e6f2b-43b4-5f27-a225-2ae0170fa8cd","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c70e6f2b-43b4-5f27-a225-2ae0170fa8cd/attachment.yaml","path":"examples/templates.yaml","size":6384,"sha256":"6a5545372378b52baa2851cf44fc565d688fb08ca173b6dbcc3928531c84bd60","contentType":"application/yaml; charset=utf-8"},{"id":"311be2db-57bd-5603-bd45-1323457b4141","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/311be2db-57bd-5603-bd45-1323457b4141/attachment.md","path":"references/best-practices.md","size":6378,"sha256":"81704e7ff16ef5e1e5af190db5714902a91a756903be18a79e695ca181bf2849","contentType":"text/markdown; charset=utf-8"},{"id":"4fbb4e01-1848-57f0-8466-3cc6a8027752","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4fbb4e01-1848-57f0-8466-3cc6a8027752/attachment.md","path":"references/blueprints.md","size":11751,"sha256":"9ee5127976a966e3bb6456163fa193820a286d9a33a566dcc36974c9e65dd049","contentType":"text/markdown; charset=utf-8"},{"id":"a2b67b39-4a83-5f96-81cb-696470863ce3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a2b67b39-4a83-5f96-81cb-696470863ce3/attachment.md","path":"references/lovelace.md","size":14043,"sha256":"df8e205def7e3a26a21dd853122d8c1f9c241c18a3a4089de83c6950c9837532","contentType":"text/markdown; charset=utf-8"},{"id":"63ef7edc-c9f7-54ba-b2c8-95e59670b621","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/63ef7edc-c9f7-54ba-b2c8-95e59670b621/attachment.md","path":"references/patterns.md","size":21119,"sha256":"585443e0261b0c24db0a4ab0372018eaf7c619f7c3ea08b2627b1876556edec1","contentType":"text/markdown; charset=utf-8"},{"id":"1774e6cd-3707-5943-a22f-e9e2426aa863","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1774e6cd-3707-5943-a22f-e9e2426aa863/attachment.md","path":"references/templates.md","size":15637,"sha256":"aef717825923ad9a1e09416ea7d001d2b18aeccba785f23b7f3855f44462f870","contentType":"text/markdown; charset=utf-8"},{"id":"66f6f5cd-2cc0-5c32-bf4e-4b536fe14ad2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/66f6f5cd-2cc0-5c32-bf4e-4b536fe14ad2/attachment.md","path":"references/troubleshooting.md","size":5919,"sha256":"9a6b8c9ed41d02149be6b68617574b2acf787f77419e4c7c684635824d8fbc6f","contentType":"text/markdown; charset=utf-8"},{"id":"98a4ee00-6dab-52be-8098-489fcb36af8c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/98a4ee00-6dab-52be-8098-489fcb36af8c/attachment.py","path":"scripts/check_config.py","size":9802,"sha256":"da54eb0fcfcd042c07d3ddeab4466f7950cc6db22cab6ae3a9ea83079477d1f4","contentType":"text/x-python; charset=utf-8"},{"id":"09bb1056-32ca-5aab-b2d9-dbf6d06f926f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/09bb1056-32ca-5aab-b2d9-dbf6d06f926f/attachment.py","path":"scripts/find_duplicates.py","size":20669,"sha256":"99f3a9ea71c502d090448e3b30b8cb8acb2e0ff2991bf6769495b0303b62a796","contentType":"text/x-python; charset=utf-8"},{"id":"2c2f578f-2578-574e-b348-e5c72dbbf2d6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2c2f578f-2578-574e-b348-e5c72dbbf2d6/attachment.py","path":"scripts/lovelace_validator.py","size":12412,"sha256":"3d840096de8c3b9f41cc197096802ff2c17e1d3723ed067b3ab906eba4c71d3a","contentType":"text/x-python; charset=utf-8"},{"id":"7f4649aa-bcdd-541c-a04d-7344ec8362d6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7f4649aa-bcdd-541c-a04d-7344ec8362d6/attachment.py","path":"scripts/validate_yaml.py","size":5340,"sha256":"e971bf1b03b5fa6cffc3a31b99f159117ccfc0e33da5906c0bbeecc2d741807b","contentType":"text/x-python; charset=utf-8"},{"id":"ccd29af8-d3a8-5046-9781-518c8da3c59d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ccd29af8-d3a8-5046-9781-518c8da3c59d/attachment.json","path":"skill-report.json","size":12890,"sha256":"28ac13c4cfcffb8c84c43bd6a9286bf068150b842ef242eb2e655c30ad8c30f8","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"30d7f5e6045edc167f1c61e5fcb5b0503571e52821da738a2675b576843a0264","attachment_count":18,"text_attachments":18,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/esjavadex/homeassistant-config/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"data-analytics","category_label":"Data"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"data-analytics","import_tag":"clean-skills-v1","description":"Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, Lovelace dashboards, and file organization. Use when working with Home Assistant configuration files (.yaml, .yml) or discussing HA automations, scripts, sensors, or dashboards."}},"renderedAt":1782981238505}

Home Assistant Configuration Skill Create and manage Home Assistant YAML configuration files including automations, scripts, templates, blueprints, and file organization. Slash Commands | Command | Description | |---------|-------------| | | Find duplicate automations and scripts in configuration | /ha-find-duplicates Scans Home Assistant configuration files to find: - Exact duplicates : Automations/scripts with identical triggers and actions - Similar items : Items with 80%+ similarity (name, entities, structure) - Trigger conflicts : Multiple automations responding to the same trigger Usage…