{"id":14007,"date":"2025-12-10T16:54:14","date_gmt":"2025-12-10T16:54:14","guid":{"rendered":"https:\/\/www.rjt.org.uk\/home\/?post_type=home_assistant_tip&#038;p=14007"},"modified":"2025-12-10T16:54:16","modified_gmt":"2025-12-10T16:54:16","slug":"lovelace-search","status":"publish","type":"home_assistant_tip","link":"https:\/\/www.rjt.org.uk\/home\/archives\/home-assistant-tip\/lovelace-search\/","title":{"rendered":"Lovelace Search"},"content":{"rendered":"\n<p>In my system I often download dashboard components from HACS and then can&#8217;t remember if I have used them.<br>So I built a simple dashboard page to search the lovelace files in .storage for any plain text string and output the results as a markdown card.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Lovelace Search Setup Instructions<\/h1>\n\n\n\n<p>Complete setup guide for searching Lovelace configuration files in Home Assistant using MQTT.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Home Assistant with MQTT broker configured (Mosquitto add-on recommended)<\/li>\n\n\n\n<li>File Editor add-on (for editing configuration files)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Add Input Text Helper<\/h2>\n\n\n\n<p>Add this to your <code>configuration.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">input_text:\n  lovelace_search_term:\n    name: Search Term\n    initial: \"type:\"\n    max: 255\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Add Shell Command<\/h2>\n\n\n\n<p>Add this to your <code>configuration.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">shell_command:\n  search_lovelace: \/bin\/bash -c \"grep -HnF '{{ search_string }}' \/config\/.storage\/lovelace* 2>&amp;1 || echo 'No matches found'\"\n<\/pre>\n\n\n\n<p><strong>Note:<\/strong> The <code>-F<\/code> flag treats the search as a literal string (not regex), so searches like <code>button-card<\/code> will work correctly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Configure MQTT Sensor<\/h2>\n\n\n\n<p>Add this to your <code>configuration.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">mqtt:\n  sensor:\n    - name: \"Lovelace Search Results\"\n      state_topic: \"temp\/lovelace_search\/state\"\n      unique_id: lovelace_search_results\n      json_attributes_topic: \"temp\/lovelace_search\/results\"  \n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Create Search Script<\/h2>\n\n\n\n<p>Add this to your <code>configuration.yaml<\/code> (or create in UI under Settings &gt; Automations &amp; Scenes &gt; Scripts):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">sequence:\n  - data:\n      search_string: \"{{search_text}}\"\n    action: shell_command.search_lovelace\n    response_variable: result_text\n  - variables:\n      lines: |\n        {{ result_text.stdout.split('\\n') }}\n      parsed_data: >\n        {% set ns = namespace(files={}) %} {% for line in lines %}\n          {% if line and line.strip() != '' and ':' in line %}\n            {% set parts = line.split(':', 2) %}\n            {% if parts | length >= 3 %}\n              {% set file = parts[0].split('\/')[-1] | string %}\n              {% set line_num = parts[1] | string %}\n              {% set content = parts[2].strip() | string %}\n              {% if file in ns.files %}\n                {% set ns.files = dict(ns.files, **{file: ns.files[file] + [{'line': line_num, 'content': content}]}) %}\n              {% else %}\n                {% set ns.files = dict(ns.files, **{file: [{'line': line_num, 'content': content}]}) %}\n              {% endif %}\n            {% endif %}\n          {% endif %}\n        {% endfor %} {% set total = namespace(count=0) %} {% for file, matches\n        in ns.files.items() %}\n          {% set total.count = total.count + (matches | length) %}\n        {% endfor %} {\n          \"files\": {{ ns.files | tojson }},\n          \"search_term\": \"{{ search_text }}\",\n          \"total_matches\": {{ total.count }},\n          \"file_count\": {{ ns.files | length }}\n        }\n  - action: mqtt.publish\n    metadata: {}\n    data:\n      evaluate_payload: false\n      qos: 0\n      retain: false\n      topic: temp\/lovelace_search\/results\n      payload: |\n        {{parsed_data}}\n  - action: mqtt.publish\n    metadata: {}\n    data:\n      evaluate_payload: false\n      qos: 0\n      retain: false\n      topic: temp\/lovelace_search\/state\n      payload: |\n        {{search_text}}\nalias: Search Lovelace\ndescription: \"\"\nfields:\n  search_text:\n    selector:\n      text: null\n    name: Search Text\n    description: Search Lovelace GUI Dashboards for this value\n    required: true\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Restart Home Assistant<\/h2>\n\n\n\n<p>After adding all the configuration:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Go to <strong>Developer Tools > YAML<\/strong><\/li>\n\n\n\n<li>Click <strong>Check Configuration<\/strong><\/li>\n\n\n\n<li>If valid, click <strong>Restart<\/strong><\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Create Dashboard Card<\/h2>\n\n\n\n<p>Add this card to any dashboard:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">type: vertical-stack\ncards:\n  - type: entities\n    title: Lovelace File Search\n    entities:\n      - entity: input_text.lovelace_search_term\n        name: Search Term\n  \n  - type: button\n    name: Search\n    icon: mdi:magnify\n    tap_action:\n      action: call-service\n      service: script.search_lovelace_files\n  \n  - type: markdown\n    title: Search Results\n    content: |\n      {% set data = state_attr('sensor.lovelace_search_results', 'files') %}\n      {% set total = state_attr('sensor.lovelace_search_results', 'total_matches') %}\n      {% set term = states('sensor.lovelace_search_results') %}\n      \n      # ? Lovelace Search Results\n      \n      {% if data and total > 0 %}\n      **Search term:** `{{ term }}`  \n      **Found {{ total }} match(es) in {{ data | length }} file(s)**\n      \n      {% for file, matches in data.items() %}\n      ## ? {{ file }}\n      *{{ matches | length }} match(es)*\n      \n      {% for match in matches %}\n      * **{{ match.line }}:** {{ match.content[:100] }}{{ '...' if match.content | length > 100 }}\n      {% endfor %}\n      \n      ---\n      {% endfor %}\n      {% else %}\n      *No matches found or search not yet run*\n      {% endif %}\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How to Use<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Enter a search term in the input field (e.g., <code>button-card<\/code>, <code>type: entities<\/code>, <code>custom:mushroom<\/code>)<\/li>\n\n\n\n<li>Click the <strong>Search<\/strong> button<\/li>\n\n\n\n<li>View formatted results showing:\n<ul class=\"wp-block-list\">\n<li>Which files contain matches<\/li>\n\n\n\n<li>Line numbers where matches occur<\/li>\n\n\n\n<li>Content of matching lines<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Example Searches<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>button-card<\/code> &#8211; Find all button card instances<\/li>\n\n\n\n<li><code>entities<\/code> &#8211; Find all entities cards<\/li>\n\n\n\n<li><code>custom:<\/code> &#8211; Find all custom cards<\/li>\n\n\n\n<li><code>entity_id<\/code> &#8211; Find entity references<\/li>\n\n\n\n<li><code>sensor.<\/code> &#8211; Find all sensor entities<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">No results showing:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Check MQTT broker is running<\/li>\n\n\n\n<li>Verify sensor exists: Developer Tools > States > <code>sensor.lovelace_search_results<\/code><\/li>\n\n\n\n<li>Check script ran successfully in Logbook<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Search not finding expected results:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Remember searches are case-sensitive<\/li>\n\n\n\n<li>Try broader search terms (e.g., <code>button<\/code> instead of <code>button-card<\/code>)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">MQTT sensor unavailable:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Verify MQTT integration is configured<\/li>\n\n\n\n<li>Check Mosquitto broker add-on is running<\/li>\n\n\n\n<li>Restart Home Assistant<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">File Structure<\/h2>\n\n\n\n<p>Your Lovelace configuration files are stored in:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/config\/.storage\/lovelace*\n<\/pre>\n\n\n\n<p>These files include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>lovelace<\/code> &#8211; Main dashboard<\/li>\n\n\n\n<li><code>lovelace.dashboard_*<\/code> &#8211; Named dashboards<\/li>\n\n\n\n<li><code>lovelace_dashboards<\/code> &#8211; Dashboard registry<\/li>\n\n\n\n<li><code>lovelace_resources<\/code> &#8211; Custom resources<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Technical Details<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">How It Works<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Shell Command<\/strong>: Uses <code>grep<\/code> to search through all Lovelace storage files<\/li>\n\n\n\n<li><strong>Script Processing<\/strong>: Parses grep output and converts to structured JSON<\/li>\n\n\n\n<li><strong>MQTT Publishing<\/strong>: Stores results in MQTT for persistence<\/li>\n\n\n\n<li><strong>Markdown Display<\/strong>: Formats results into a readable table<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">JSON Structure<\/h3>\n\n\n\n<p>The search results are stored in this format:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n  \"files\": {\n    \"lovelace.dashboard_name\": [\n      {\n        \"line\": \"42\",\n        \"content\": \"type: custom:button-card\"\n      }\n    ]\n  },\n  \"search_term\": \"button-card\",\n  \"total_matches\": 15,\n  \"file_count\": 3\n}\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Customization Options<\/h3>\n\n\n\n<p><strong>Change content length display:<\/strong> Modify <code>[:100]<\/code> in the markdown card to show more or fewer characters:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">* **{{ match.line }}:** {{ match.content[:200] }}\n<\/pre>\n\n\n\n<p><strong>Add case-insensitive search:<\/strong> Modify shell command to add <code>-i<\/code> flag:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -HnFi '{{ search_string }}'\n<\/pre>\n\n\n\n<p><strong>Search specific dashboards only:<\/strong> Modify the file pattern in shell command:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/config\/.storage\/lovelace.dashboard_*\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Credits<\/h2>\n\n\n\n<p>This solution uses Home Assistant&#8217;s native capabilities combined with standard Linux tools to provide a powerful search interface for Lovelace configuration files.<\/p>\n","protected":false},"template":"","class_list":["post-14007","home_assistant_tip","type-home_assistant_tip","status-publish","hentry","comments-off"],"_links":{"self":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/home_assistant_tip\/14007","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/home_assistant_tip"}],"about":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/types\/home_assistant_tip"}],"wp:attachment":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/media?parent=14007"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}