{
    "version": "https://jsonfeed.org/version/1",
    "title": "Home Assistant Developer Docs Blog",
    "home_page_url": "https://developers.home-assistant.io/blog",
    "description": "Home Assistant Developer Docs Blog",
    "items": [
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine",
            "content_html": "<p>Since Home Assistant Core 2026.9, schema validation runs on <a href=\"https://probatio.frenck.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Probatio</a> instead of voluptuous. This went in quietly because it was meant to change nothing, and for most integrations it changed nothing. That is worth saying out loud anyway, along with what it does change and what you can now reach for.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"your-existing-code-keeps-working\">Your existing code keeps working<a href=\"https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine#your-existing-code-keeps-working\" class=\"hash-link\" aria-label=\"Direct link to Your existing code keeps working\" title=\"Direct link to Your existing code keeps working\" translate=\"no\">​</a></h2>\n<p>Probatio is a clean-room reimplementation of voluptuous with the same public API. <code>import voluptuous as vol</code> still works: Home Assistant aliases the name in <code>sys.modules</code> at startup, so the import resolves to Probatio. Custom integrations need no changes, and voluptuous is no longer installed at all.</p>\n<p>Core itself has moved to importing Probatio directly, and <code>import voluptuous</code> is now banned there by a lint rule. That ban applies to our own source. Your integration can keep the old import for as long as you like.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-we-switched\">Why we switched<a href=\"https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine#why-we-switched\" class=\"hash-link\" aria-label=\"Direct link to Why we switched\" title=\"Direct link to Why we switched\" translate=\"no\">​</a></h2>\n<p>voluptuous has been effectively unmaintained for a long time, and it sits under every integration in Home Assistant. Probatio is maintained, MIT licensed, pure Python, and holds behavioral compatibility with voluptuous as its primary correctness target.</p>\n<p>It is also quicker. The project measures the interpreted engine at roughly 2.3 to 3.4 times voluptuous, and an optional compiled path at roughly 6.7 to 7.4 times, on <a href=\"https://probatio.frenck.dev/reference/performance/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">its own benchmarks</a>. Those are single-machine numbers and the project says not to treat them as guarantees, so take them as a direction rather than a promise. Home Assistant builds a great many schemas and validates a fraction of them per run, so we set a lazy build policy: a schema compiles when it is first validated, not when it is constructed.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-changed-for-you\">What changed for you<a href=\"https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine#what-changed-for-you\" class=\"hash-link\" aria-label=\"Direct link to What changed for you\" title=\"Direct link to What changed for you\" translate=\"no\">​</a></h2>\n<p>Two things are worth knowing, because both produced real issues after 2026.9.</p>\n<p><strong>Error messages are worded differently.</strong> If your tests assert on validation error text, they may need updating. Probatio also suggests a close match for an unknown key, and 2026.9.1 raised the bar for when it offers one, so weak guesses no longer appear.</p>\n<p><strong>Validation is stricter in a few places, which surfaced data that was quietly wrong before.</strong> The clearest case was the KNX config store, which was validated before writing but not on load. Entries written by hand or by third-party tooling had been passed through untouched for years, and started failing at setup. The data was always invalid. Nothing told anyone until something checked.</p>\n<p>If your integration stores structured data and only validates it on the way in, this is worth a look.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-you-can-use-now\">What you can use now<a href=\"https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine#what-you-can-use-now\" class=\"hash-link\" aria-label=\"Direct link to What you can use now\" title=\"Direct link to What you can use now\" translate=\"no\">​</a></h2>\n<p>The compatibility layer only exposes voluptuous's surface, so reaching any of this means importing <code>probatio</code> directly.</p>\n<p><strong>Typing that survives the call.</strong> Validators that hand their input back keep the caller's type, so <code>probatio.EnsureList()(names)</code> on a <code>list[str]</code> gives you a <code>list[str]</code> rather than a <code>list[Any]</code>.</p>\n<p><strong>Dataclass and TypedDict schemas.</strong> Build schemas from annotations. <code>DataclassSchema</code> returns a typed dataclass instance, while <code>TypedDictSchema</code> returns a validated dict typed as the <code>TypedDict</code>:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> dataclasses </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> dataclass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> probatio</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token decorator annotation punctuation\" style=\"color:rgb(199, 146, 234)\">@dataclass</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(255, 203, 107)\">Server</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    host</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    port</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">int</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(247, 140, 108)\">80</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">schema </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> probatio</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">DataclassSchema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">Server</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"host\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"nas\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"port\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(247, 140, 108)\">8080</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Server(host=\"nas\", port=8080)</span><br></div></code></pre></div></div>\n<p><strong>Markers voluptuous never had.</strong> <code>Secret</code> redacts a key's value from error output, which matters when a schema holds a password. <code>Forbidden</code> requires a key's absence, <code>Alias</code> accepts a value under an old name and emits it under the canonical one, and <code>TaggedUnion</code> routes on one key's value instead of trying every branch and reporting all of them.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">probatio</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        probatio</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Required</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">probatio</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Secret</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_PASSWORD</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p><strong>Structured errors, including translation keys.</strong> Every <code>Invalid</code> carries a stable <code>code</code>, a <code>translation_key</code>, <code>placeholders</code> for interpolation, the <code>path</code> to the offending value, and a <code>secret</code> flag. <code>as_dict()</code> serializes the lot. That means a validation failure can be branched on programmatically and rendered in the user's language, rather than parsed out of an English sentence.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"code\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"length\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"message\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"length of value must be at least 12\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"path\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"password\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"secret\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"context\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"translation_key\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"length_min\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"placeholders\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"min\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(247, 140, 108)\">12</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><br></div></code></pre></div></div>\n<p><strong>Cross-field rules</strong>, like <code>AtLeastOne</code>, <code>ExactlyOne</code>, <code>AllOrNone</code>, <code>RequiredWith</code> and <code>RequiredIf</code>, which previously had to be hand-rolled per integration.</p>\n<p><strong>Codecs.</strong> <code>to_json_schema</code>, <code>to_openapi</code> and <code>from_openapi</code> convert between a schema and a document, which is how we describe LLM tools.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"documentation-and-a-note-for-ai-tooling\">Documentation, and a note for AI tooling<a href=\"https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine#documentation-and-a-note-for-ai-tooling\" class=\"hash-link\" aria-label=\"Direct link to Documentation, and a note for AI tooling\" title=\"Direct link to Documentation, and a note for AI tooling\" translate=\"no\">​</a></h2>\n<p>The full documentation lives at <a href=\"https://probatio.frenck.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">probatio.frenck.dev</a>, including a <a href=\"https://probatio.frenck.dev/reference/compatibility-matrix/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">compatibility matrix</a> listing everything Probatio adds beyond voluptuous.</p>\n<p>The site also publishes <a href=\"https://probatio.frenck.dev/llms.txt\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">llms.txt</a>, a machine-readable index of the documentation. If you use an AI assistant while working on an integration, pointing it at that file is worth doing. Probatio is newer than most model training data, so an assistant left to its own devices will write voluptuous, guess at the API, or invent a validator that does not exist. There are abridged and complete variants alongside it, plus separate indexes for the guides, the recipes and the API reference.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/30/probatio-validation-engine",
            "title": "Probatio is our validation engine",
            "summary": "Since Home Assistant Core 2026.9, schema validation runs on Probatio instead of voluptuous. This went in quietly because it was meant to change nothing, and for most integrations it changed nothing. That is worth saying out loud anyway, along with what it does change and what you can now reach for.",
            "date_modified": "2026-09-30T00:00:00.000Z",
            "author": {
                "name": "Franck Nijhof",
                "url": "https://twitter.com/frenck"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons",
            "content_html": "<p>As of Home Assistant Core 2026.10, the <code>homeassistant</code> integration can translate abort reasons that every integration words the same way. If you're today linking from a local translation key, for an abort reason, to a shared translation key under the <code>homeassistant</code> integration, you can instead just rely on the shared translation key under the <code>homeassistant</code> domain directly. This is done by default in some helpers and can also be done explicitly by setting the <code>translation_domain</code> parameter when aborting the flow.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-to-do\">What to do<a href=\"https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons#what-to-do\" class=\"hash-link\" aria-label=\"Direct link to What to do\" title=\"Direct link to What to do\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">Delete the keys below from the <code>abort</code> sections of <code>strings.json</code>, including those under <code>config_subentries</code>, only when the abort uses the <code>homeassistant</code> translation domain.</li>\n<li class=\"\">The helpers listed below use the central translation domain automatically.</li>\n<li class=\"\">If your code passes one of these reasons to <code>async_abort</code> or <code>AbortFlow</code>, pass the <code>homeassistant</code> translation domain before deleting the local key. Without that domain, retain the local key. See <a href=\"https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons#raise-one-yourself\" class=\"\">Raise one yourself</a>.</li>\n</ul>\n<p>When an abort uses the central translation domain, the frontend resolves the reason from that domain and does not use a local key.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"default-reasons\">Default reasons<a href=\"https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons#default-reasons\" class=\"hash-link\" aria-label=\"Direct link to Default reasons\" title=\"Direct link to Default reasons\" translate=\"no\">​</a></h2>\n<table><thead><tr><th>Reason</th><th>Raised by</th></tr></thead><tbody><tr><td><code>already_in_progress</code></td><td><code>async_set_unique_id</code>, discovery without a unique ID</td></tr><tr><td><code>single_instance_allowed</code></td><td><code>single_config_entry</code>, <code>DiscoveryFlowHandler</code>, <code>WebhookFlowHandler</code></td></tr><tr><td><code>no_devices_found</code></td><td><code>DiscoveryFlowHandler</code></td></tr><tr><td><code>cloud_not_connected</code></td><td><code>WebhookFlowHandler</code></td></tr><tr><td><code>reauth_successful</code></td><td><code>async_update_reload_and_abort</code>, <code>async_update_and_abort</code></td></tr><tr><td><code>reconfigure_successful</code></td><td><code>async_update_reload_and_abort</code>, <code>async_update_and_abort</code>, also for subentries</td></tr><tr><td><code>authorize_url_timeout</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>missing_credentials</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>no_url_available</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>oauth_error</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>oauth_failed</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>oauth_implementation_unavailable</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>oauth_timeout</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>oauth_unauthorized</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr><tr><td><code>user_rejected_authorize</code></td><td><code>AbstractOAuth2FlowHandler</code></td></tr></tbody></table>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"explicit-translation-domain\">Explicit translation domain<a href=\"https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons#explicit-translation-domain\" class=\"hash-link\" aria-label=\"Direct link to Explicit translation domain\" title=\"Direct link to Explicit translation domain\" translate=\"no\">​</a></h2>\n<p>Pass the domain that owns the string. Both <code>async_abort</code> and <code>AbortFlow</code> accept it.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">core </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> DOMAIN </span><span class=\"token keyword\" style=\"font-style:italic\">as</span><span class=\"token plain\"> HOMEASSISTANT_DOMAIN</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_abort</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    reason</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"no_devices_found\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    translation_domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">HOMEASSISTANT_DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>This works in config flows and subentry flows. Options flows look in the <code>options</code> section, which the <code>homeassistant</code> integration does not have.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"keep-your-own-wording\">Keep your own wording<a href=\"https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons#keep-your-own-wording\" class=\"hash-link\" aria-label=\"Direct link to Keep your own wording\" title=\"Direct link to Keep your own wording\" translate=\"no\">​</a></h2>\n<p>Only needed if you word a reason differently on purpose:</p>\n<ul>\n<li class=\"\">Pass <code>reason</code> to <code>async_update_reload_and_abort</code> or <code>async_update_and_abort</code>, even with the default name.</li>\n<li class=\"\">In an OAuth2 flow, pass <code>translation_domain=DOMAIN</code>.</li>\n</ul>\n<p>Aborts that core raises by itself, like <code>already_in_progress</code>, always use the central string, to keep a customized string there, overwrite the steps that produce the strings upstream.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/28/central-config-flow-abort-reasons",
            "title": "Shared config flow abort reasons are translated centrally",
            "summary": "As of Home Assistant Core 2026.10, the homeassistant integration can translate abort reasons that every integration words the same way. If you're today linking from a local translation key, for an abort reason, to a shared translation key under the homeassistant integration, you can instead just rely on the shared translation key under the homeassistant domain directly. This is done by default in some helpers and can also be done explicitly by setting the translation_domain parameter when aborting the flow.",
            "date_modified": "2026-09-28T00:00:00.000Z",
            "author": {
                "name": "Josef Zweck",
                "url": "https://github.com/zweckj"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/26/llm-tool-result",
            "content_html": "<p>As of Home Assistant Core 2026.10, an LLM tool returns an <code>llm.ToolResult</code> instead of a plain JSON object. <code>ToolResult</code> carries the tool's <code>data</code> and an <code>error</code> flag that says whether the call failed. On the chat log side, <code>ToolResultContent.tool_result</code> is replaced by <code>ToolResultContent.result</code>, which holds the <code>ToolResult</code>.</p>\n<p>Returning a plain JSON object from a tool, reading <code>ToolResultContent.tool_result</code>, and setting <code>tool_result</code> on a tool result delta are deprecated. They keep working for custom integrations with a warning in the log until Home Assistant Core 2027.11, and stop working after that.</p>\n<p>The release also adds metadata to <code>llm.Tool</code>. A tool now has a <code>title</code> for people to read, <code>annotations</code> that describe how it behaves, and an <code>integration</code> that records which integration provides it. The MCP Server integration serves the title and the annotations to MCP clients.</p>\n<p>The annotations are an <code>llm.ToolAnnotations</code> with four flags: <code>read_only</code>, <code>destructive</code>, <code>idempotent</code> and <code>open_world</code>. The defaults describe the least safe case, so a tool that declares nothing is taken to write, to be destructive, and to reach outside Home Assistant.</p>\n<p>Creating a tool without an <code>integration</code> is deprecated. A core integration raises an error. A custom integration gets a warning in the log until Home Assistant Core 2027.10, and stops working after that.</p>\n<p>Both changes together look like this:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">core </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> HomeAssistant</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">helpers </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> llm</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">const </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> DOMAIN</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(255, 203, 107)\">GetItemsTool</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Tool</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Tool to read the items on a list.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    name </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"my_integration__get_items\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    title </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Get list items\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    description </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Read the items on a list.\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    integration </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> DOMAIN</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    annotations </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">ToolAnnotations</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        read_only</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> destructive</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">False</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> idempotent</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> open_world</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">False</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_call</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        tool_input</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">ToolInput</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        llm_context</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">LLMContext</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">ToolResult</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Call the tool.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> llm</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">ToolResult</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">data</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"items\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Milk\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Bread\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>",
            "url": "https://developers.home-assistant.io/blog/2026/09/26/llm-tool-result",
            "title": "LLM tools return a ToolResult and declare their integration",
            "summary": "As of Home Assistant Core 2026.10, an LLM tool returns an llm.ToolResult instead of a plain JSON object. ToolResult carries the tool's data and an error flag that says whether the call failed. On the chat log side, ToolResultContent.tool_result is replaced by ToolResultContent.result, which holds the ToolResult.",
            "date_modified": "2026-09-26T00:00:00.000Z",
            "author": {
                "name": "Paulus Schoutsen",
                "url": "https://github.com/balloob"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/17/Use-config-entry-exc-in-migration",
            "content_html": "<p>Integration migration <code>async_migrate_entry</code> method can now raise config entry exceptions instead of only being limited to return <code>False</code>. This is preferred to provide more context and being translatable.</p>\n<p>Integrations that fetch data from other services during migration can raise <code>ConfigEntryNotReady</code> to have the migration retried automatically later (as during setup), which helps with timeouts and other errors that may resolve on their own.</p>\n<p>By returning <code>False</code> or raising any other exception than <code>ConfigEntryNotReady</code> the migration stops and the config entry state is set to <code>migration_error</code> which is a non-recoverable state.\nThis previously required the user to restart Home Assistant to retry the migration.\nThe new correct way to handle it is to create a repair issue. After the user fixes the issue, call <code>hass.config_entries.async_retry_migration(entry_id)</code> to retry the migration.</p>\n<p>More info in the <a class=\"\" href=\"https://developers.home-assistant.io/docs/core/integration/config_flow#handle-returns-raise-exceptions-in-migrations\">config flow documentation</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example-migration-function-which-raises-if-client-has-an-issue\">Example migration function which raises if client has an issue<a href=\"https://developers.home-assistant.io/blog/2026/09/17/Use-config-entry-exc-in-migration#example-migration-function-which-raises-if-client-has-an-issue\" class=\"hash-link\" aria-label=\"Direct link to Example migration function which raises if client has an issue\" title=\"Direct link to Example migration function which raises if client has an issue\" translate=\"no\">​</a></h2>\n<p>The migration function lives in the integration's <code>__init__.py</code>.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">helpers</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">issue_registry </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> IssueSeverity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> async_create_issue</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_migrate_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> ConfigEntry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Migrate old entry.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    _LOGGER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">debug</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Migrating configuration from version %s.%s\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">version</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">minor_version</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">version </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">==</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(247, 140, 108)\">1</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">try</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            new_info </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">await</span><span class=\"token plain\"> client</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">get_info</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">except</span><span class=\"token plain\"> ConnectionError </span><span class=\"token keyword\" style=\"font-style:italic\">as</span><span class=\"token plain\"> exc</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"font-style:italic\">raise</span><span class=\"token plain\"> ConfigEntryNotReady</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                translation_key</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"key\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                translation_domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> exc</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">except</span><span class=\"token plain\"> AuthenticationError </span><span class=\"token keyword\" style=\"font-style:italic\">as</span><span class=\"token plain\"> exc</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            async_create_issue</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                </span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">f\"migrate_could_not_auth_</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string-interpolation interpolation\">config_entry</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token string-interpolation interpolation\">entry_id</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                is_fixable</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                is_persistent</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">False</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                severity</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">IssueSeverity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">ERROR</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                translation_key</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"migrate_could_not_auth\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                translation_placeholders</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"title\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">title</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                data</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"entry_id\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"font-style:italic\">raise</span><span class=\"token plain\"> ConfigEntryError</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">translation_key</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"migrate_could_not_auth\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> exc</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        new_data </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">**</span><span class=\"token plain\">config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"some_data\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> new_info</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"some_data\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_update_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> data</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">new_data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> version</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token number\" style=\"color:rgb(247, 140, 108)\">2</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    _LOGGER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">debug</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Migration to configuration version %s.%s successful\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">version</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">minor_version</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><br></div></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example-repair-which-will-retry-the-migration\">Example repair which will retry the migration<a href=\"https://developers.home-assistant.io/blog/2026/09/17/Use-config-entry-exc-in-migration#example-repair-which-will-retry-the-migration\" class=\"hash-link\" aria-label=\"Direct link to Example repair which will retry the migration\" title=\"Direct link to Example repair which will retry the migration\" translate=\"no\">​</a></h2>\n<p>The repair flow lives in the integration's <code>repairs.py</code>.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(255, 203, 107)\">MigrationRepairFlow</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">RepairsFlow</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Handler for a migration repair flow.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">__init__</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Initialize the flow.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> entry_id</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_step_init</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> user_input</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">dict</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> Any</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> RepairsFlowResult</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Fix the issue and retry the migration.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> user_input </span><span class=\"token keyword\" style=\"font-style:italic\">is</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">not</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># TODO: Do whatever is necessary to fix the issue,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># for example update the config entry data</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"font-style:italic\">await</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_retry_migration</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_create_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">data</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_show_form</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            step_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"init\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            data_schema</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">probatio</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_create_fix_flow</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    issue_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">dict</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">int</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">float</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> RepairsFlow</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Create flow.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> issue_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">startswith</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"migrate_could_not_auth_\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> MigrationRepairFlow</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"entry_id\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>",
            "url": "https://developers.home-assistant.io/blog/2026/09/17/Use-config-entry-exc-in-migration",
            "title": "Use Config Entry exceptions in integration migration method",
            "summary": "Integration migration asyncmigrateentry method can now raise config entry exceptions instead of only being limited to return False. This is preferred to provide more context and being translatable.",
            "date_modified": "2026-09-17T00:00:00.000Z",
            "author": {
                "name": "G Johansson",
                "url": "https://github.com/gjohansson-ST"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary\">Summary<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\" translate=\"no\">​</a></h2>\n<p>The device registry's multi-config-entry compatibility properties — <code>DeviceEntry.config_entries</code>, <code>DeviceEntry.config_entries_subentries</code> and <code>DeviceEntry.primary_config_entry</code> — were announced as deprecated in <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry\">Devices are restricted to a single config entry and at most one subentry</a>, but reading them was still silent. They now report at runtime: core and core integrations raise <code>RuntimeError</code>, custom integrations log a warning.</p>\n<p>Use <code>DeviceEntry.config_entry_id</code> and <code>DeviceEntry.config_subentry_id</code> instead. The properties remain available to custom integrations until Home Assistant Core 2027.10, two releases later than the 2027.8 given in the earlier post.</p>\n<p>Reading the properties on a synthesized composite device is not deprecated and does not report, because such a device really does span several config entries.</p>\n<p><strong>Most integrations don't read these properties and don't need any changes.</strong> Read on if your integration inspects a device's config entries, or accesses them on a deleted or child device.</p>\n<p>This is implemented in core <a href=\"https://github.com/home-assistant/core/pull/181949\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #181949</a> and lands in Home Assistant Core 2026.10.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-reports-and-what-to-use-instead\">What reports, and what to use instead<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#what-reports-and-what-to-use-instead\" class=\"hash-link\" aria-label=\"Direct link to What reports, and what to use instead\" title=\"Direct link to What reports, and what to use instead\" translate=\"no\">​</a></h2>\n<table><thead><tr><th>Deprecated</th><th>Replacement</th></tr></thead><tbody><tr><td><code>DeviceEntry.config_entries</code></td><td><code>DeviceEntry.config_entry_id</code></td></tr><tr><td><code>DeviceEntry.config_entries_subentries</code></td><td><code>DeviceEntry.config_entry_id</code> and <code>DeviceEntry.config_subentry_id</code></td></tr><tr><td><code>DeviceEntry.primary_config_entry</code></td><td><code>DeviceEntry.config_entry_id</code></td></tr></tbody></table>\n<p>As with the other device registry deprecations, the enforcement is stricter inside core: a core or core integration caller raises <code>RuntimeError</code> immediately, while a custom integration keeps working with a logged warning until Home Assistant Core 2027.10.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"migrating\">Migrating<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#migrating\" class=\"hash-link\" aria-label=\"Direct link to Migrating\" title=\"Direct link to Migrating\" translate=\"no\">​</a></h2>\n<p>A device belongs to exactly one config entry and at most one config subentry, so each of the shims collapses to a plain attribute read:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Before</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">entry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">primary_config_entry</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># After</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">entry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entry_id</span><br></div></code></pre></div></div>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Before</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id </span><span class=\"token keyword\" style=\"font-style:italic\">in</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># After</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">==</span><span class=\"token plain\"> config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><br></div></code></pre></div></div>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Before</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">for</span><span class=\"token plain\"> entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> subentry_ids </span><span class=\"token keyword\" style=\"font-style:italic\">in</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries_subentries</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">items</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">for</span><span class=\"token plain\"> subentry_id </span><span class=\"token keyword\" style=\"font-style:italic\">in</span><span class=\"token plain\"> subentry_ids</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># After</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">entry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entry_id</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">subentry_id </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_subentry_id</span><br></div></code></pre></div></div>\n<p>If you loop over a device's config entries to find the one belonging to your integration, use the <code>async_get_device_and_config_entry_for_domain()</code> helper introduced in <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes\">More device registry deprecations, new helpers and validation</a> instead:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> dr</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_device_and_config_entry_for_domain</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">DOMAIN</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Core's own test suite was migrated off the properties in core <a href=\"https://github.com/home-assistant/core/pull/181945\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #181945</a>, which can serve as a set of examples.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"composite-devices-are-exempt\">Composite devices are exempt<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#composite-devices-are-exempt\" class=\"hash-link\" aria-label=\"Direct link to Composite devices are exempt\" title=\"Direct link to Composite devices are exempt\" translate=\"no\">​</a></h2>\n<p>The properties are not deprecated for a synthesized composite device — the read-only device <code>DeviceRegistry.async_get()</code> returns for a pre-migration composite device id, as described in <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#backwards-compatibility\">Backwards compatibility</a>. Such a device spans several config entries, which <code>config_entry_id</code> and <code>config_subentry_id</code> can't represent, so the three properties are the correct API for it and reading them does not report, for any caller.</p>\n<p>The exemption is keyed on <code>DeviceEntry.is_composite_device</code>, which code handling both kinds of device can branch on. Core's <code>device_automation</code> integration is a genuine example: the device ids passed to <code>async_get_device_automations()</code> come from automations, scripts and the frontend, so they can still be pre-migration composite ids, and listing a device's automations needs the domain of every config entry owning it:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># homeassistant/components/device_automation/__init__.py</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:=</span><span class=\"token plain\"> device_registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">is</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">raise</span><span class=\"token plain\"> DeviceNotFound</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">is_composite_device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    entry_ids </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">else</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    entry_ids </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\">device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">for</span><span class=\"token plain\"> entry_id </span><span class=\"token keyword\" style=\"font-style:italic\">in</span><span class=\"token plain\"> entry_ids</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">if</span><span class=\"token plain\"> config_entry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:=</span><span class=\"token plain\"> hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">config_entries</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        domain_devices</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">setdefault</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">domain</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">set</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">add</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Note that this is only needed for code which deliberately accepts a stored device id that may predate the migration. Code that only ever handles devices it registered itself should read <code>config_entry_id</code> unconditionally.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deleted-devices\">Deleted devices<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#deleted-devices\" class=\"hash-link\" aria-label=\"Direct link to Deleted devices\" title=\"Direct link to Deleted devices\" translate=\"no\">​</a></h2>\n<p><code>DeletedDeviceEntry.config_entries</code> and <code>DeletedDeviceEntry.config_entries_subentries</code> are deprecated and report on the same terms. A deleted device is never a composite, so there is no exemption — every read reports.</p>\n<p>Use <code>DeletedDeviceEntry.config_entry_id</code> and <code>DeletedDeviceEntry.config_subentry_id</code>. Both are <code>None</code> for an orphaned deleted device, that is, one whose owning config entry has been removed; the deprecated properties represented that case as an empty set and an empty dict.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"child-devices\">Child devices<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#child-devices\" class=\"hash-link\" aria-label=\"Direct link to Child devices\" title=\"Direct link to Child devices\" translate=\"no\">​</a></h2>\n<p><code>ChildDeviceEntry</code> inherits the three properties, and a child device is never a composite either, so reading them on a child device always reports. A child device belongs to the same config entry and subentry as its parent, both available as <code>config_entry_id</code> and <code>config_subentry_id</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-websocket-api-and-stored-data-are-unaffected\">The WebSocket API and stored data are unaffected<a href=\"https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation#the-websocket-api-and-stored-data-are-unaffected\" class=\"hash-link\" aria-label=\"Direct link to The WebSocket API and stored data are unaffected\" title=\"Direct link to The WebSocket API and stored data are unaffected\" translate=\"no\">​</a></h2>\n<p>This change only concerns the Python properties. A regular device is serialized as before — <code>config/device_registry/list</code>, the <code>EVENT_DEVICE_REGISTRY_UPDATED</code> payload and the device registry storage still carry the <code>config_entries</code>, <code>config_entries_subentries</code> and <code>primary_config_entry</code> fields, computed internally without reporting — so WebSocket clients are not affected and see no new warnings. Child devices never carried these fields in the first place.</p>\n<p>Those fields are deprecated on their own schedule, described in <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#new-device-fields-config_entry_id-and-config_subentry_id\">Device registry WebSocket API changes</a>.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/15/device-entry-config-entries-deprecation",
            "title": "`DeviceEntry.config_entries` deprecation is now enforced",
            "summary": "Summary",
            "date_modified": "2026-09-15T00:00:00.000Z",
            "author": {
                "name": "Erik Montnemery",
                "url": "https://github.com/emontnemery"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/11/lawn-mower-stop-action-idle-activity",
            "content_html": "<p>As of Home Assistant Core 2026.10, <code>LawnMowerEntity</code> supports a <code>stop</code> action and an <code>IDLE</code> activity.</p>\n<p><code>stop</code> cancels the current mowing task without returning the mower to the dock. It differs from <code>pause</code>, which keeps the task so it can be resumed, and from <code>dock</code>, which sends the mower home. To support it, add <code>LawnMowerEntityFeature.STOP</code> to the supported features and implement <code>stop</code> or <code>async_stop</code>.</p>\n<p><code>LawnMowerActivity.IDLE</code> describes a mower that is stopped, but neither docked nor paused. Integrations that mapped such a state to <code>PAUSED</code> or <code>ERROR</code> should now use <code>IDLE</code>.</p>\n<p>More details can be found in the <a class=\"\" href=\"https://developers.home-assistant.io/docs/core/entity/lawn-mower\">documentation</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example\">Example<a href=\"https://developers.home-assistant.io/blog/2026/09/11/lawn-mower-stop-action-idle-activity#example\" class=\"hash-link\" aria-label=\"Direct link to Example\" title=\"Direct link to Example\" translate=\"no\">​</a></h2>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(255, 203, 107)\">MyLawnMower</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">LawnMowerEntity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    _attr_supported_features </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        LawnMowerEntityFeature</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">START_MOWING</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> LawnMowerEntityFeature</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">DOCK</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> LawnMowerEntityFeature</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">STOP</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_stop</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Stop the mower and cancel the current task.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"font-style:italic\">await</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">mower</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">stop</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">_attr_activity </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> LawnMowerActivity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">IDLE</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_write_ha_state</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>",
            "url": "https://developers.home-assistant.io/blog/2026/09/11/lawn-mower-stop-action-idle-activity",
            "title": "New stop action and idle activity for lawn mowers",
            "summary": "As of Home Assistant Core 2026.10, LawnMowerEntity supports a stop action and an IDLE activity.",
            "date_modified": "2026-09-11T00:00:00.000Z",
            "author": {
                "name": "Paul Bottein",
                "url": "https://github.com/piitaya"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling",
            "content_html": "<p>As of Home Assistant Core 2026.10, the OAuth2 helper raises exceptions that config entry setup already understands, so integrations no longer translate OAuth2 failures themselves.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-changed\">What changed<a href=\"https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling#what-changed\" class=\"hash-link\" aria-label=\"Direct link to What changed\" title=\"Direct link to What changed\" translate=\"no\">​</a></h2>\n<p>The OAuth2 exceptions now inherit from the config entry exception that describes what should happen, and carry a translated message from the <code>homeassistant</code> integration.</p>\n<table><thead><tr><th>Exception</th><th>Also a</th><th>Result when uncaught</th></tr></thead><tbody><tr><td><code>ImplementationUnavailableError</code></td><td><code>ConfigEntryNotReady</code></td><td>Setup is retried</td></tr><tr><td><code>UnknownImplementationError</code></td><td><code>ConfigEntryAuthFailed</code></td><td>Reauth flow starts</td></tr><tr><td><code>OAuth2TokenRequestError</code></td><td><code>ConfigEntryNotReady</code></td><td>Setup is retried</td></tr><tr><td><code>OAuth2TokenRequestTransientError</code></td><td><code>ConfigEntryNotReady</code></td><td>Setup is retried</td></tr><tr><td><code>OAuth2TokenRequestConnectionError</code></td><td><code>ConfigEntryNotReady</code></td><td>Setup is retried</td></tr><tr><td><code>OAuth2TokenRequestReauthError</code></td><td><code>ConfigEntryAuthFailed</code></td><td>Reauth flow starts</td></tr></tbody></table>\n<p>All of them are importable from <code>homeassistant.exceptions</code> and carry a translated user-facing message, so the integration does not need a <code>strings.json</code> entry for them.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"migration\">Migration<a href=\"https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling#migration\" class=\"hash-link\" aria-label=\"Direct link to Migration\" title=\"Direct link to Migration\" translate=\"no\">​</a></h2>\n<div class=\"language-diff codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-diff codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-    try:</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        implementation = await async_get_config_entry_implementation(hass, entry)</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-    except ImplementationUnavailableError as err:</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        raise ConfigEntryNotReady(</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-            translation_domain=DOMAIN,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-            translation_key=\"oauth2_implementation_unavailable\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        ) from err</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">+    implementation = await async_get_config_entry_implementation(hass, entry)</span><br></div></code></pre></div></div>\n<p>Also remove the now-unused <code>oauth2_implementation_unavailable</code> entry from the <code>exceptions</code> section of <code>strings.json</code>.</p>\n<p>Catching <code>ValueError</code> around this call can go as well. <code>UnknownImplementationError</code> subclasses <code>ValueError</code> for backwards compatibility, so a <code>ValueError</code> handler silently downgrades it and discards the translated message.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"token-requests\">Token requests<a href=\"https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling#token-requests\" class=\"hash-link\" aria-label=\"Direct link to Token requests\" title=\"Direct link to Token requests\" translate=\"no\">​</a></h3>\n<div class=\"language-diff codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-diff codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-    try:</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        await auth.async_get_access_token()</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-    except OAuth2TokenRequestReauthError as err:</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        raise ConfigEntryAuthFailed from err</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-    except OAuth2TokenRequestError as err:</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">-        raise ConfigEntryNotReady from err</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">+    await auth.async_get_access_token()</span><br></div></code></pre></div></div>\n<p>The same applies to <code>await session.async_ensure_token_valid()</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"quality-scale\">Quality scale<a href=\"https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling#quality-scale\" class=\"hash-link\" aria-label=\"Direct link to Quality scale\" title=\"Direct link to Quality scale\" translate=\"no\">​</a></h2>\n<p>The <a class=\"\" href=\"https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/test-before-setup\"><code>test-before-setup</code></a> rule accepts <code>await session.async_ensure_token_valid()</code> in <code>async_setup_entry</code> as satisfying the rule, alongside <code>await coordinator.async_config_entry_first_refresh()</code>. Both raise the appropriate config entry exception on the integration's behalf.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"when-to-keep-catching\">When to keep catching<a href=\"https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling#when-to-keep-catching\" class=\"hash-link\" aria-label=\"Direct link to When to keep catching\" title=\"Direct link to When to keep catching\" translate=\"no\">​</a></h2>\n<p>Central handling is a default, not a restriction. Keep an explicit handler when the integration genuinely needs different behavior, for example:</p>\n<ul>\n<li class=\"\">Treating a specific status as permanent with <code>ConfigEntryError</code> instead of retrying.</li>\n<li class=\"\">Adding context to the message that only the integration knows.</li>\n<li class=\"\">Cleaning up integration state before the exception propagates.</li>\n</ul>\n<p>In those cases, catch the most specific exception that applies and let the rest propagate.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/07/oauth2-central-error-handling",
            "title": "OAuth2 error handling moved into the helper",
            "summary": "As of Home Assistant Core 2026.10, the OAuth2 helper raises exceptions that config entry setup already understands, so integrations no longer translate OAuth2 failures themselves.",
            "date_modified": "2026-09-07T00:00:00.000Z",
            "author": {
                "name": "Josef Zweck",
                "url": "https://github.com/zweckj"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors",
            "content_html": "<p>New <a href=\"https://www.home-assistant.io/docs/blueprint/selectors/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">selectors</a> are available to choose a device class or sensor state class.</p>\n<p>These selectors can be used in config flows and blueprints when requesting a device or state class from the user.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"device-class-selector\">Device class selector<a href=\"https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors#device-class-selector\" class=\"hash-link\" aria-label=\"Direct link to Device class selector\" title=\"Direct link to Device class selector\" translate=\"no\">​</a></h2>\n<p>The new <code>DeviceClassSelector</code> is available for selecting device classes in config flows and blueprints. It supports device classes for the following platforms:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">BINARY_SENSOR</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> BinarySensorDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">BUTTON</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> ButtonDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">COVER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> CoverDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">EVENT</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> EventDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">HUMIDIFIER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HumidifierDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">INFRARED</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> InfraredDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">MEDIA_PLAYER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> MediaPlayerDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">NUMBER</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> NumberDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">SENSOR</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> SensorDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">SWITCH</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> SwitchDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">UPDATE</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> UpdateDeviceClass</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">VALVE</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> ValveDeviceClass</span><br></div></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"device-class-selector-examples\">Device class selector examples<a href=\"https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors#device-class-selector-examples\" class=\"hash-link\" aria-label=\"Direct link to Device class selector examples\" title=\"Direct link to Device class selector examples\" translate=\"no\">​</a></h3>\n<p>Example of a device class selector that returns a single device class:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Optional</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_DEVICE_CLASS</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> DeviceClassSelector</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            DeviceClassSelectorConfig</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">SENSOR</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Example of a device class selector that returns multiple device classes as a list:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Optional</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_DEVICE_CLASS</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> DeviceClassSelector</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            DeviceClassSelectorConfig</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">Platform</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">BINARY_SENSOR</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                multiple</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"sensor-state-class-selector\">Sensor state class selector<a href=\"https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors#sensor-state-class-selector\" class=\"hash-link\" aria-label=\"Direct link to Sensor state class selector\" title=\"Direct link to Sensor state class selector\" translate=\"no\">​</a></h2>\n<p>The new <code>StateClassSelector</code> is available for selecting sensor state classes in config flows and blueprints.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"sensor-state-class-selector-examples\">Sensor state class selector examples<a href=\"https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors#sensor-state-class-selector-examples\" class=\"hash-link\" aria-label=\"Direct link to Sensor state class selector examples\" title=\"Direct link to Sensor state class selector examples\" translate=\"no\">​</a></h3>\n<p>Example of a sensor state class selector that returns a single state class:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Optional</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_STATE_CLASS</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> StateClassSelector</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Example of a sensor state class selector that returns a single state class with only a filtered subset of available state classes:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Optional</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_STATE_CLASS</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> StateClassSelector</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            StateClassSelectorConfig</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                state_classes</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                    SensorStateClass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">MEASUREMENT</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                    SensorStateClass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">TOTAL_INCREASING</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">                </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Example of a state class selector that returns multiple state classes as a list:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Schema</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        vol</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">Optional</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_STATE_CLASS</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> StateClassSelector</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            StateClassSelectorConfig</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">multiple</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"migrating-existing-device-and-state-class-selectors\">Migrating existing device and state class selectors<a href=\"https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors#migrating-existing-device-and-state-class-selectors\" class=\"hash-link\" aria-label=\"Direct link to Migrating existing device and state class selectors\" title=\"Direct link to Migrating existing device and state class selectors\" translate=\"no\">​</a></h2>\n<p>Unlike using a generic <code>SelectSelector</code>, the <code>DeviceClassSelector</code> allows the frontend to automatically translate device classes into user-friendly names.</p>\n<p>Existing implementations that select a device or state class using <code>SelectSelector</code> should be migrated to use <code>DeviceClassSelector</code> or <code>StateClassSelector</code>. After migration, any stale translations related to the old selector values should be removed.</p>\n<p>The <a href=\"https://www.home-assistant.io/docs/blueprint/selectors/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Selectors</a> documentation has been updated to include the new selectors.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/04/device-and-state-class-selectors",
            "title": "New device and state class selectors",
            "summary": "New selectors are available to choose a device class or sensor state class.",
            "date_modified": "2026-09-04T00:00:00.000Z",
            "author": {
                "name": "Jan Bouwhuis",
                "url": "https://github.com/jbouwh"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/09/02/modbus-get-hub-deprecation",
            "content_html": "<p>As of Home Assistant Core 2026.10, <code>modbus.get_hub</code> is deprecated. It will be removed in Home Assistant Core 2027.10. Custom integrations that call it get a warning in the log until then, and stop working after that.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"background\">Background<a href=\"https://developers.home-assistant.io/blog/2026/09/02/modbus-get-hub-deprecation#background\" class=\"hash-link\" aria-label=\"Direct link to Background\" title=\"Direct link to Background\" translate=\"no\">​</a></h2>\n<p><code>get_hub</code> attaches an integration to a Modbus hub the user configured in YAML, under a name the integration has to be told. The user has to set up the hub by hand before the integration can work, and two integrations that need the same bus cannot share it.</p>\n<p>In July we announced our plan to <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus\">modernize Modbus in Home Assistant</a>. Home Assistant Core 2026.9 delivered the first piece: <code>async_get_unit</code>. An integration collects the connection details in its own config flow, the same as any other integration, and asks the Modbus integration for a unit on them. Integrations that ask with equal details share one connection. Nothing is configured in YAML and nothing extra is persisted.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-to-do\">What to do<a href=\"https://developers.home-assistant.io/blog/2026/09/02/modbus-get-hub-deprecation#what-to-do\" class=\"hash-link\" aria-label=\"Direct link to What to do\" title=\"Direct link to What to do\" translate=\"no\">​</a></h2>\n<p>Replace the call to <code>get_hub</code> with a call to <code>async_get_unit</code>:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">components</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">modbus </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> async_get_unit</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> modbus_connection </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> ModbusTcpParams</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_setup_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> MyConfigEntry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">bool</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Set up my device from a config entry.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    unit </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> async_get_unit</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        ModbusTcpParams</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">host</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\">CONF_HOST</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> port</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\">CONF_PORT</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\">CONF_UNIT_ID</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    device </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> MyDevice</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">unit</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><br></div></code></pre></div></div>\n<p>This is not a one-to-one swap. Your config flow has to collect the connection details required by the transport, for example host and port, besides unit ID, that the user used to write in the YAML hub, and the device-specific communication should move into a library built on <a href=\"https://home-assistant-libs.github.io/modbus-connection/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>modbus-connection</code></a>. The <a class=\"\" href=\"https://developers.home-assistant.io/docs/modbus/introduction\">Modbus developer documentation</a> describes both, with example code and a reference device library.</p>\n<p>More details can be found in the <a href=\"https://github.com/home-assistant/core/pull/179933\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">core PR</a>.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/09/02/modbus-get-hub-deprecation",
            "title": "Deprecating modbus.get_hub in favor of async_get_unit",
            "summary": "As of Home Assistant Core 2026.10, modbus.get_hub is deprecated. It will be removed in Home Assistant Core 2027.10. Custom integrations that call it get a warning in the log until then, and stop working after that.",
            "date_modified": "2026-09-02T00:00:00.000Z",
            "author": {
                "name": "Paulus Schoutsen",
                "url": "https://github.com/balloob"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/08/31/deprecate-configurator",
            "content_html": "<p>The Configurator integration has been deprecated and will be removed in Home Assistant 2027.10. The integration was originally created to provide a web-based configuration interface for Home Assistant, but it is no longer recommended for use. No core integrations use the Configurator integration anymore, and it is not recommended for custom integrations either.</p>\n<p>The modern way to configure integrations in Home Assistant is via config flows and config entries. <a href=\"https://developers.home-assistant.io/docs/core/integration/config_flow\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Config flows</a> provide a user-friendly interface for setting up and configuring integrations, while config entries allow for easy management of integration settings.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/08/31/deprecate-configurator",
            "title": "Configurator integration is now deprecated",
            "summary": "The Configurator integration has been deprecated and will be removed in Home Assistant 2027.10. The integration was originally created to provide a web-based configuration interface for Home Assistant, but it is no longer recommended for use. No core integrations use the Configurator integration anymore, and it is not recommended for custom integrations either.",
            "date_modified": "2026-08-31T00:00:00.000Z",
            "author": {
                "name": "Martin Hjelmare",
                "url": "https://github.com/MartinHjelmare"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary\">Summary<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\" translate=\"no\">​</a></h2>\n<p>This is a follow-up to <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry\">Devices are restricted to a single config entry and at most one subentry</a>, and covers additional device registry deprecations, a few new helper methods, and some stricter validation that landed after that post.</p>\n<p><strong>Most custom integrations won't be affected by this set of changes.</strong> Read on if your integration sets <code>via_device</code> or <code>default_manufacturer</code> / <code>default_model</code> / <code>default_name</code> in <code>DeviceInfo</code>, looks devices up in the registry, reads the registry's <code>devices</code>, <code>deleted_devices</code> or <code>child_devices</code> containers, calls <code>async_update_device</code> directly, or attaches a device to an entity that has no config entry or unique id.</p>\n<p>Unless noted otherwise, deprecated functionality logs a warning at runtime and remains supported until Home Assistant Core 2027.8. As before, deprecations which are only relevant to core and core integrations are enforced more strictly there: those callers raise immediately, while custom integrations keep getting a warning until the removal version.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"via_device-follow-ups\"><code>via_device</code> follow-ups<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#via_device-follow-ups\" class=\"hash-link\" aria-label=\"Direct link to via_device-follow-ups\" title=\"Direct link to via_device-follow-ups\" translate=\"no\">​</a></h2>\n<p>The previous post deprecated <code>DeviceInfo[\"via_device\"]</code> and <code>DeviceRegistry.async_get_or_create(via_device=...)</code> in favor of <code>via_device_id</code>, because identifiers are only unique per config entry and no longer point at a single device. A few related changes build on that.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"looking-up-a-via-device-id-async_get_device_id_by_identifier\">Looking up a via device id: <code>async_get_device_id_by_identifier()</code><a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#looking-up-a-via-device-id-async_get_device_id_by_identifier\" class=\"hash-link\" aria-label=\"Direct link to looking-up-a-via-device-id-async_get_device_id_by_identifier\" title=\"Direct link to looking-up-a-via-device-id-async_get_device_id_by_identifier\" translate=\"no\">​</a></h3>\n<p>To set <code>via_device_id</code> you need the device id of the via device. The new helper <code>homeassistant.helpers.device_registry.async_get_device_id_by_identifier()</code> looks it up, scoped to the config entry that owns the via device:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token decorator annotation punctuation\" style=\"color:rgb(199, 146, 234)\">@callback</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_get_device_id_by_identifier</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> identifier</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">tuple</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">*</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><br></div></code></pre></div></div>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">via_device_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">dr</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_device_id_by_identifier</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> hub_serial_number</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>The lookup is unambiguous because identifiers are unique within a config entry. It raises <code>ValueError</code> if no matching device exists, so only call it once the via device has been created. When your integration creates the via device itself, skip the lookup and read <code>.id</code> from the <code>DeviceEntry</code> that <code>async_get_or_create</code> returned for it.</p>\n<p>This helper was added in core <a href=\"https://github.com/home-assistant/core/pull/177494\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #177494</a>, which also migrated a number of integrations from <code>via_device</code> to <code>via_device_id</code> as examples.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"via_device-removed-from-the-deviceinfo-type-and-attr_via_device-deprecated\"><code>via_device</code> removed from the <code>DeviceInfo</code> type, and <code>ATTR_VIA_DEVICE</code> deprecated<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#via_device-removed-from-the-deviceinfo-type-and-attr_via_device-deprecated\" class=\"hash-link\" aria-label=\"Direct link to via_device-removed-from-the-deviceinfo-type-and-attr_via_device-deprecated\" title=\"Direct link to via_device-removed-from-the-deviceinfo-type-and-attr_via_device-deprecated\" translate=\"no\">​</a></h3>\n<p>Two typing-level follow-ups from core <a href=\"https://github.com/home-assistant/core/pull/178465\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178465</a>:</p>\n<ul>\n<li class=\"\"><code>via_device</code> has been removed from the <code>DeviceInfo</code> <code>TypedDict</code>. A <code>DeviceInfo</code> literal that still sets <code>via_device</code> is now a typing error; use <code>via_device_id</code>.</li>\n<li class=\"\">The constant <code>homeassistant.const.ATTR_VIA_DEVICE</code> is deprecated. Importing it logs a warning; use the string <code>\"via_device_id\"</code> instead. It is removed in Home Assistant Core 2027.8.</li>\n</ul>\n<p>The same PR also escalates the enforcement of the deprecations announced in the previous post — <code>DeviceRegistry.async_get_device()</code>, passing <code>via_device</code> to <code>async_get_or_create()</code>, and the <code>add_config_entry_id</code> / <code>add_config_subentry_id</code> / <code>remove_config_entry_id</code> / <code>remove_config_subentry_id</code> parameters of <code>async_update_device()</code>. Core and core integrations now raise <code>RuntimeError</code> when calling these, while custom integrations continue to log a warning until Home Assistant Core 2027.8.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"a-device-cant-be-its-own-via-device\">A device can't be its own via device<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#a-device-cant-be-its-own-via-device\" class=\"hash-link\" aria-label=\"Direct link to A device can't be its own via device\" title=\"Direct link to A device can't be its own via device\" translate=\"no\">​</a></h3>\n<p>A device may no longer set itself as its own via device (core <a href=\"https://github.com/home-assistant/core/pull/178194\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178194</a>):</p>\n<ul>\n<li class=\"\">Passing <code>via_device_id</code> equal to the device's own id to <code>async_get_or_create()</code> or <code>async_update_device()</code> raises <code>HomeAssistantError</code>.</li>\n<li class=\"\">Passing the deprecated <code>via_device</code> referencing the device itself is ignored and logged now, and will raise from Home Assistant Core 2027.8.</li>\n</ul>\n<p>The device registry storage is bumped to version 3.3 (introduced in Home Assistant Core 2026.8), which clears any existing <code>via_device_id</code> self-references on load. Integrations which previously worked around this by clearing the self-reference themselves can drop that code.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"async_update_device-merge_connections-and-merge_identifiers-deprecated\"><code>async_update_device()</code>: <code>merge_connections</code> and <code>merge_identifiers</code> deprecated<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#async_update_device-merge_connections-and-merge_identifiers-deprecated\" class=\"hash-link\" aria-label=\"Direct link to async_update_device-merge_connections-and-merge_identifiers-deprecated\" title=\"Direct link to async_update_device-merge_connections-and-merge_identifiers-deprecated\" translate=\"no\">​</a></h2>\n<p>Passing <code>merge_connections</code> or <code>merge_identifiers</code> to <code>DeviceRegistry.async_update_device()</code> is deprecated in core <a href=\"https://github.com/home-assistant/core/pull/178888\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178888</a>. These parameters only add to the device's existing connections or identifiers; compute the full desired set yourself and pass it as <code>new_connections</code> or <code>new_identifiers</code> instead.</p>\n<p>Core and core integrations raise <code>RuntimeError</code> when passing the merge parameters; custom integrations log a warning until Home Assistant Core 2027.9 (note this removal is one release later than the others).</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"finding-a-devices-config-entry-for-a-domain-async_get_device_and_config_entry_for_domain\">Finding a device's config entry for a domain: <code>async_get_device_and_config_entry_for_domain()</code><a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#finding-a-devices-config-entry-for-a-domain-async_get_device_and_config_entry_for_domain\" class=\"hash-link\" aria-label=\"Direct link to finding-a-devices-config-entry-for-a-domain-async_get_device_and_config_entry_for_domain\" title=\"Direct link to finding-a-devices-config-entry-for-a-domain-async_get_device_and_config_entry_for_domain\" translate=\"no\">​</a></h2>\n<p>A common pattern was to fetch a device and loop over its <code>config_entries</code> to find the config entry belonging to a given integration. Since <code>DeviceEntry.config_entries</code> is deprecated, core <a href=\"https://github.com/home-assistant/core/pull/178991\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178991</a> adds a helper that does this directly:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token decorator annotation punctuation\" style=\"color:rgb(199, 146, 234)\">@callback</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_get_device_and_config_entry_for_domain</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">*</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> domain</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">tuple</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\">DeviceEntry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> ConfigEntry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><br></div></code></pre></div></div>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> config_entry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> dr</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_device_and_config_entry_for_domain</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">DOMAIN</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>It returns <code>(None, None)</code> for an unknown or child-device id, and <code>(device, None)</code> when a main device exists but no config entry of <code>domain</code> owns it. For a pre-migration composite id it returns a matching split device and its config entry, or the restored composite device and <code>None</code> when no split matches. The helper does not check whether the config entry is loaded, so keep your own <code>ConfigEntryState.LOADED</code> check if you need one.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-devices-deleted_devices-and-child_devices-containers-are-protected\">The <code>devices</code>, <code>deleted_devices</code> and <code>child_devices</code> containers are protected<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#the-devices-deleted_devices-and-child_devices-containers-are-protected\" class=\"hash-link\" aria-label=\"Direct link to the-devices-deleted_devices-and-child_devices-containers-are-protected\" title=\"Direct link to the-devices-deleted_devices-and-child_devices-containers-are-protected\" translate=\"no\">​</a></h2>\n<p>The <code>DeviceRegistry.devices</code>, <code>DeviceRegistry.deleted_devices</code> and <code>DeviceRegistry.child_devices</code> attributes used to be dictionaries keyed by device id, and integrations sometimes read them directly — <code>registry.devices.get(device_id)</code>, <code>registry.devices.values()</code>, or <code>device_id in registry.devices</code>. The backing containers are now protected, and the public attributes are narrowed to what an integration legitimately needs.</p>\n<p><code>devices</code> (core <a href=\"https://github.com/home-assistant/core/pull/179578\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179578</a>) is now a read-only collection of the registered devices. Iterating it yields the <code>DeviceEntry</code> values directly, and <code>len()</code> and value membership (<code>device_entry in registry.devices</code>) work as expected. Using it as a mapping — subscription, <code>.get()</code>, <code>.values()</code>, <code>.keys()</code>, or membership by device id (<code>device_id in registry.devices</code>) — is deprecated. The mapping surface keeps working as a backwards compatibility shim: core and core integrations raise <code>RuntimeError</code>, while custom integrations only log a warning. It is removed in Home Assistant Core 2027.9.</p>\n<p>In practice, iterate with <code>for device in registry.devices</code> instead of <code>registry.devices.values()</code>, and look a device up by id with <code>registry.async_get(device_id)</code> instead of <code>registry.devices.get(device_id)</code>.</p>\n<p><code>child_devices</code> (core <a href=\"https://github.com/home-assistant/core/pull/179713\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179713</a>) is now a read-only collection of the child devices. It follows the container protocol — you can iterate it (it yields the child device entries), take its <code>len()</code>, and test value membership — and offers no dictionary-style access at all: there is no <code>.get()</code> or <code>.values()</code> and no lookup by device id. There is no backwards compatibility shim.</p>\n<p><code>deleted_devices</code> (core <a href=\"https://github.com/home-assistant/core/pull/179720\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179720</a>) is an internal implementation detail of the device registry, and the whole attribute is now deprecated — there is no supported public use. Accessing it in any way is deprecated: core and core integrations raise <code>RuntimeError</code>, while custom integrations only log a warning. It is removed in Home Assistant Core 2027.9.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"testing-what-kind-of-device-an-id-refers-to\">Testing what kind of device an id refers to<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#testing-what-kind-of-device-an-id-refers-to\" class=\"hash-link\" aria-label=\"Direct link to Testing what kind of device an id refers to\" title=\"Direct link to Testing what kind of device an id refers to\" translate=\"no\">​</a></h3>\n<p>With direct container access going away, <code>DeviceRegistry.async_get</code> gains an <code>include_composite_devices</code> parameter to test what an id refers to (core <a href=\"https://github.com/home-assistant/core/pull/179594\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179594</a>). With <code>include_composite_devices=False</code>, a pre-migration composite device id resolves to <code>None</code>:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">is_composite </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">is</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">not</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">and</span><span class=\"token plain\"> registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> include_composite_devices</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">False</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">is</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>This complements the existing <code>include_child_devices</code> and <code>include_main_devices</code> parameters, which similarly resolve — or exclude — child and main devices. <code>DeviceRegistry.async_is_composite_device_id</code>, introduced in the previous post, is now redundant and deprecated in the same PR; use <code>async_get</code> with <code>include_composite_devices=False</code> instead. It keeps working — core and core integrations raise <code>RuntimeError</code>, while custom integrations only log a warning — until Home Assistant Core 2027.9.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"attaching-a-device-to-an-entity-requires-a-config-entry-and-a-unique-id\">Attaching a device to an entity requires a config entry and a unique id<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#attaching-a-device-to-an-entity-requires-a-config-entry-and-a-unique-id\" class=\"hash-link\" aria-label=\"Direct link to Attaching a device to an entity requires a config entry and a unique id\" title=\"Direct link to Attaching a device to an entity requires a config entry and a unique id\" translate=\"no\">​</a></h2>\n<p>An entity may only attach a device if it has a unique id and belongs to a config entry. Attempting to attach a device from an entity without a config entry, or without a unique id, is now caught in <code>entity_platform</code> (core <a href=\"https://github.com/home-assistant/core/pull/177459\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #177459</a>): the device link is dropped and a warning is logged. This is only a warning for now, and will raise from Home Assistant Core 2027.8.</p>\n<p>Note that the device link is already dropped today, even though the raise only lands in 2027.8 — an affected entity ends up with no device until it is given a unique id and a config entry.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"device-info-types-are-removed\">Device info types are removed<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#device-info-types-are-removed\" class=\"hash-link\" aria-label=\"Direct link to Device info types are removed\" title=\"Direct link to Device info types are removed\" translate=\"no\">​</a></h2>\n<p>The device registry used to classify each <code>DeviceInfo</code> as one of a fixed set of \"device info types\" (link, primary, secondary) based on which keys it contained. This existed to guess the primary integration for a device shared across several config entries, which is obsolete now that a device is owned by a single config entry. The classification is removed and replaced by lighter validation in core <a href=\"https://github.com/home-assistant/core/pull/179397\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179397</a>, with the following developer-visible consequences:</p>\n<ul>\n<li class=\"\"><code>DeviceInfo</code> shapes that previously didn't match any type are now accepted. The old error <em>\"device info needs to either describe a device, link to existing device or provide extra information\"</em> is gone; a <code>DeviceInfo</code> still needs at least one of <code>identifiers</code> or <code>connections</code>.</li>\n<li class=\"\">Passing both a field and its <code>default_</code> counterpart — for example both <code>name</code> and <code>default_name</code>, or <code>model</code> and <code>default_model</code> — now raises <code>DeviceInfoError</code>.</li>\n<li class=\"\">When a new device is created without a name, it now defaults to the config entry title for <strong>all</strong> device infos, not just those that were previously classified as primary. This can change the composed name of entities on devices that previously had no name.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"default_manufacturer-default_model-and-default_name-are-deprecated\"><code>default_manufacturer</code>, <code>default_model</code> and <code>default_name</code> are deprecated<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#default_manufacturer-default_model-and-default_name-are-deprecated\" class=\"hash-link\" aria-label=\"Direct link to default_manufacturer-default_model-and-default_name-are-deprecated\" title=\"Direct link to default_manufacturer-default_model-and-default_name-are-deprecated\" translate=\"no\">​</a></h2>\n<p>The <code>default_manufacturer</code>, <code>default_model</code> and <code>default_name</code> members of <code>DeviceInfo</code> are deprecated in core <a href=\"https://github.com/home-assistant/core/pull/179549\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179549</a>. Pass the plain <code>manufacturer</code>, <code>model</code> and <code>name</code> fields instead.</p>\n<p>These <code>default_</code> fields let an integration that wasn't considered the primary one for a shared device suggest a name, manufacturer or model without overwriting the value set by the primary integration. A device now belongs to a single config entry, so there is no longer a primary integration to defer to, and the plain fields can be set directly.</p>\n<p>The three fields have been removed from the <code>DeviceInfo</code> type, so setting them is a typing error, and passing any of them to <code>async_get_or_create</code> logs a warning. As with the other device registry deprecations, core and core integrations raise <code>RuntimeError</code>, while custom integrations keep working with a warning until Home Assistant Core 2027.9.</p>\n<p>Entities which set these fields — device trackers, for example, set <code>default_name</code> — should pass the non-default variant instead:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Before</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">DeviceInfo</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    connections</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONNECTION_NETWORK_MAC</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> mac</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    default_name</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">f\"Device </span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string-interpolation interpolation\">mac</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># After</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">DeviceInfo</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    connections</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONNECTION_NETWORK_MAC</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> mac</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">f\"Device </span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token string-interpolation interpolation\">mac</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(195, 232, 141)\">\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"passing-created_at-or-modified_at-to-async_get_or_create-is-deprecated\">Passing <code>created_at</code> or <code>modified_at</code> to <code>async_get_or_create</code> is deprecated<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#passing-created_at-or-modified_at-to-async_get_or_create-is-deprecated\" class=\"hash-link\" aria-label=\"Direct link to passing-created_at-or-modified_at-to-async_get_or_create-is-deprecated\" title=\"Direct link to passing-created_at-or-modified_at-to-async_get_or_create-is-deprecated\" translate=\"no\">​</a></h2>\n<p><code>created_at</code> and <code>modified_at</code> are managed by the device registry itself, and passing them to <code>DeviceRegistry.async_get_or_create</code> never had any effect — the values were ignored. Passing either is now deprecated in core <a href=\"https://github.com/home-assistant/core/pull/179998\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #179998</a>: core and core integrations raise <code>RuntimeError</code>, while custom integrations only log a warning, until Home Assistant Core 2027.9. Simply drop the arguments.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"more-examples\">More examples<a href=\"https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes#more-examples\" class=\"hash-link\" aria-label=\"Direct link to More examples\" title=\"Direct link to More examples\" translate=\"no\">​</a></h2>\n<p>The changes above land alongside a large number of per-integration adaptations — migrating to <code>via_device_id</code>, using the new lookup helpers, and removing device self-references — which can serve as further examples. Search the core repository for pull requests referencing these APIs if you need a concrete migration to follow.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/08/24/device-registry-follow-up-changes",
            "title": "More device registry deprecations, new helpers and validation",
            "summary": "Summary",
            "date_modified": "2026-08-24T00:00:00.000Z",
            "author": {
                "name": "Erik Montnemery",
                "url": "https://github.com/emontnemery"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary\">Summary<a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\" translate=\"no\">​</a></h2>\n<p>This post describes changes to the device registry <strong>WebSocket API</strong> — the commands and device serialization consumed by the frontend, custom cards, and other WebSocket clients. They stem from two changes to the device registry:</p>\n<ul>\n<li class=\"\"><strong>Restricting each device to a single config entry</strong> (Home Assistant Core 2026.8). The Python API side of this change is described in the companion post <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry\">Devices are restricted to a single config entry and at most one subentry</a>.</li>\n<li class=\"\"><strong>Introducing <a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#child-devices-in-the-device-list\" class=\"\">child devices</a></strong> (Home Assistant Core 2026.9, <a href=\"https://github.com/home-assistant/architecture/discussions/1414\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">architecture proposal #1414</a>).</li>\n</ul>\n<p>Most clients only read devices through <code>config/device_registry/list</code>, and the new device fields are additive, so no changes are required to keep working — but clients that iterate the device list should be ready to encounter child devices, which are serialized differently. Clients that remove devices, or that inspect a device's config entries, should read on.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"new-device-fields-config_entry_id-and-config_subentry_id\">New device fields: <code>config_entry_id</code> and <code>config_subentry_id</code><a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#new-device-fields-config_entry_id-and-config_subentry_id\" class=\"hash-link\" aria-label=\"Direct link to new-device-fields-config_entry_id-and-config_subentry_id\" title=\"Direct link to new-device-fields-config_entry_id-and-config_subentry_id\" translate=\"no\">​</a></h2>\n<p>Every device returned by <code>config/device_registry/list</code>, and every device in an <code>EVENT_DEVICE_REGISTRY_UPDATED</code> payload, now carries two new fields:</p>\n<ul>\n<li class=\"\"><code>config_entry_id</code> — the id of the single config entry the device belongs to.</li>\n<li class=\"\"><code>config_subentry_id</code> — the id of the single config subentry the device belongs to, or <code>null</code>.</li>\n</ul>\n<p>They replace the previous fields, which modelled a device that could span several config entries and subentries:</p>\n<ul>\n<li class=\"\"><code>config_entries</code> — a list of config entry ids.</li>\n<li class=\"\"><code>config_entries_subentries</code> — a map of config entry id to a list of subentry ids.</li>\n<li class=\"\"><code>primary_config_entry</code> — the id of the device's primary config entry.</li>\n</ul>\n<p>The old fields are <strong>kept for backwards compatibility and are deprecated</strong>; they are scheduled for removal in Home Assistant Core 2027.8. During the deprecation period they are derived from the new values: a device reports <code>config_entries</code> as the single-element list <code>[config_entry_id]</code>, <code>config_entries_subentries</code> as <code>{config_entry_id: [config_subentry_id]}</code>, and <code>primary_config_entry</code> equal to <code>config_entry_id</code>.</p>\n<p>Update your client to read <code>config_entry_id</code> and <code>config_subentry_id</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"child-devices-in-the-device-list\">Child devices in the device list<a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#child-devices-in-the-device-list\" class=\"hash-link\" aria-label=\"Direct link to Child devices in the device list\" title=\"Direct link to Child devices in the device list\" translate=\"no\">​</a></h2>\n<p>Home Assistant Core 2026.9 introduces <strong>child devices</strong> (<a href=\"https://github.com/home-assistant/architecture/discussions/1414\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">architecture proposal #1414</a>, core <a href=\"https://github.com/home-assistant/core/pull/178666\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178666</a>). A child device is a lightweight logical part of a parent device: it has no hardware or firmware metadata of its own, and it references its parent through a <code>parent_device_id</code>. The parent must be registered by the same config entry and belong to the same config subentry.</p>\n<p><code>config/device_registry/list</code> now returns child devices alongside regular devices, so its result is a mix of two kinds of entry. A child device is serialized with a smaller set of fields:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"id\": \"child1234\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"parent_device_id\": \"abcd1234\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"config_entry_id\": \"wxyz5678\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"config_subentry_id\": null,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"area_id\": null,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"name\": \"Left channel\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"name_by_user\": null,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"labels\": [],</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"identifiers\": [[\"demo\", \"left\"]],</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"disabled_by\": null,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"created_at\": 1723987200.0,</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"modified_at\": 1723987200.0</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>Compared to a regular device, a child device has <strong>no</strong> <code>connections</code>, <code>via_device_id</code>, <code>configuration_url</code>, <code>entry_type</code>, <code>manufacturer</code>, <code>model</code>, <code>model_id</code>, <code>hw_version</code>, <code>sw_version</code>, <code>serial_number</code>, <code>primary_config_entry</code>, <code>config_entries</code>, or <code>config_entries_subentries</code>. Clients that read the device list must not assume every entry carries these fields.</p>\n<p>The reliable way to tell the two apart is the <code>parent_device_id</code> field: it is present and non-null only on child devices. A regular device carries <code>via_device_id</code> instead.</p>\n<p>A child device with no <code>area_id</code> of its own inherits its parent's area, so a client resolving a child device's area should fall back to the parent device when the child's <code>area_id</code> is <code>null</code>.</p>\n<p>Two related commands also understand child devices:</p>\n<ul>\n<li class=\"\"><code>config/device_registry/update</code> accepts a child device id — setting <code>area_id</code>, <code>disabled_by</code>, <code>labels</code>, or <code>name_by_user</code> — and returns the updated child device in the reduced serialization shown above.</li>\n<li class=\"\"><code>config/device_registry/list_linked_devices</code> always returns an empty <code>linked_devices</code> list for a child device, since a child shares its parent's per-config-entry identifier namespace and is never linked to devices of other config entries.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"new-command-configdevice_registryremove\">New command: <code>config/device_registry/remove</code><a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#new-command-configdevice_registryremove\" class=\"hash-link\" aria-label=\"Direct link to new-command-configdevice_registryremove\" title=\"Direct link to new-command-configdevice_registryremove\" translate=\"no\">​</a></h2>\n<p>A new WebSocket command removes a device by id:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"type\": \"config/device_registry/remove\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"device_id\": \"abcd1234\"</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>Because a device now belongs to exactly one config entry, removing it from that config entry removes the device. The command requires admin, and it rejects a <a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#composite-devices\" class=\"\">composite device id</a> with the error <code>Cannot remove a composite device</code>.</p>\n<p>It replaces <code>config/device_registry/remove_config_entry</code>, which took both a <code>device_id</code> and a <code>config_entry_id</code>:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"type\": \"config/device_registry/remove_config_entry\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"config_entry_id\": \"wxyz5678\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"device_id\": \"abcd1234\"</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>That command still works but is <strong>deprecated</strong>: it logs a warning and will be removed in Home Assistant Core 2027.9. Its <code>config_entry_id</code> parameter is now only used to check that it matches the device's config entry — a mismatch fails with <code>Config entry not in device</code> — and the device is removed regardless of which config entry was passed. Update clients to call <code>config/device_registry/remove</code> and drop the <code>config_entry_id</code>.</p>\n<p>Implemented in core <a href=\"https://github.com/home-assistant/core/pull/178319\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #178319</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"new-command-configdevice_registrylist_linked_devices\">New command: <code>config/device_registry/list_linked_devices</code><a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#new-command-configdevice_registrylist_linked_devices\" class=\"hash-link\" aria-label=\"Direct link to new-command-configdevice_registrylist_linked_devices\" title=\"Direct link to new-command-configdevice_registrylist_linked_devices\" translate=\"no\">​</a></h2>\n<p>Because connections and identifiers are now unique per config entry, a physical device supported by several integrations is represented by one device per config entry instead of a single shared device. This command returns the other devices that share a connection or identifier with a given device — the sibling devices representing the same physical hardware under different config entries:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"type\": \"config/device_registry/list_linked_devices\",</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"device_id\": \"abcd1234\"</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>Result:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"linked_devices\": [\"ef567890\", \"12ab34cd\"]</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>The queried device itself is excluded from the result. This lets a client, for example, link from a device page to the other devices that represent the same hardware. Implemented in core <a href=\"https://github.com/home-assistant/core/pull/177449\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #177449</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"composite-devices\">New command: <code>config/device_registry/list_composite_splits</code><a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#composite-devices\" class=\"hash-link\" aria-label=\"Direct link to composite-devices\" title=\"Direct link to composite-devices\" translate=\"no\">​</a></h2>\n<p>When the device registry is loaded, each pre-migration device that spanned several config entries is split into one device per config entry, and the original (\"composite\") device id no longer refers to a registered device. Composite device ids are still referenced by automations, scripts, dashboards, and target pickers, so this command maps every composite device id to the devices it was split into:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"type\": \"config/device_registry/list_composite_splits\"</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>Result:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">{</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  \"old_composite_id\": {</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    \"split_ids\": [\"ef567890\", \"12ab34cd\"],</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    \"primary_id\": \"ef567890\"</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  }</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">}</span><br></div></code></pre></div></div>\n<p>For each composite device id, <code>split_ids</code> lists the replacement device ids and <code>primary_id</code> is the split that inherited the composite's former primary config entry, or <code>null</code>. Use it to resolve a stored composite device id to a current device — for example, to keep a device picker working when the stored id is a composite id. Implemented in core <a href=\"https://github.com/home-assistant/core/pull/176693\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176693</a>.</p>\n<p>The <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#backwards-compatibility\">Backwards compatibility</a> section of the companion post describes the rest of the deprecation-period behavior, such as actions targeting a composite device id trickling down to the split devices.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"device-registry-events\">Device registry events<a href=\"https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes#device-registry-events\" class=\"hash-link\" aria-label=\"Direct link to Device registry events\" title=\"Direct link to Device registry events\" translate=\"no\">​</a></h2>\n<p>Clients that subscribe to <code>device_registry_updated</code> events (<code>EVENT_DEVICE_REGISTRY_UPDATED</code>) see two changes, mirroring the Python API:</p>\n<ul>\n<li class=\"\">The <code>changes</code> map of an <code>update</code> event reports a device move with the keys <code>config_entry_id</code> and <code>config_subentry_id</code>, replacing the previous <code>config_entries</code> and <code>config_entries_subentries</code>.</li>\n<li class=\"\">A device now belongs to a single config entry, so it can no longer lose one config entry while remaining for another. A device losing its config entry is now a <code>remove</code> event rather than an <code>update</code>.</li>\n</ul>\n<p>See <a class=\"\" href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#device-registry-events\">Device registry events</a> in the companion post for details.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/08/19/device-registry-websocket-api-changes",
            "title": "Device registry WebSocket API changes",
            "summary": "Summary",
            "date_modified": "2026-08-19T00:00:00.000Z",
            "author": {
                "name": "Erik Montnemery",
                "url": "https://github.com/emontnemery"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"component-updates\">Component updates<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#component-updates\" class=\"hash-link\" aria-label=\"Direct link to Component updates\" title=\"Direct link to Component updates\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"ha-split-panel\">ha-split-panel<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#ha-split-panel\" class=\"hash-link\" aria-label=\"Direct link to ha-split-panel\" title=\"Direct link to ha-split-panel\" translate=\"no\">​</a></h3>\n<p>We added <code>ha-split-panel</code>, a Home Assistant wrapper around the Web Awesome split panel component.</p>\n<p>Use it when a Home Assistant page, dialog, or tool needs a resizable two-pane layout. Custom card authors can use Home Assistant frontend components, but internal Home Assistant UI APIs may change.</p>\n<div class=\"language-html codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">ha-split-panel</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(255, 203, 107)\">position</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(199, 146, 234)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(255, 85, 114)\">40</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(255, 203, 107)\">snap</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(199, 146, 234)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(255, 85, 114)\">50%</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">div</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(255, 203, 107)\">slot</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(199, 146, 234)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(255, 85, 114)\">start</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><span class=\"token plain\">Editor</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">div</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">div</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(255, 203, 107)\">slot</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(199, 146, 234)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(255, 85, 114)\">end</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(199, 146, 234)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><span class=\"token plain\">Preview</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">div</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(255, 85, 114)\">ha-split-panel</span><span class=\"token tag punctuation\" style=\"color:rgb(199, 146, 234)\">&gt;</span><br></div></code></pre></div></div>\n<p>New component-specific tokens:</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-split-panel-divider-width</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-split-panel-divider-hit-area</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-split-panel-min</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-split-panel-max</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-split-panel-grip-display</span><br></div></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"ha-tile-info-updates\">ha-tile-info updates<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#ha-tile-info-updates\" class=\"hash-link\" aria-label=\"Direct link to ha-tile-info updates\" title=\"Direct link to ha-tile-info updates\" translate=\"no\">​</a></h3>\n<p><code>ha-tile-info</code> gained more layout controls for custom cards and tile-like surfaces.</p>\n<p>New component-specific tokens:</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-tile-info-gap</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-tile-info-min-height</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-tile-info-primary-min-height</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--ha-tile-info-primary-line-clamp</span><br></div></code></pre></div></div>\n<p>Use <code>--ha-tile-info-primary-line-clamp</code> when the primary text should wrap to more than one line, and use the min-height tokens to keep rows aligned when some tiles have secondary text and others do not.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"form-and-selector-updates\">Form and selector updates<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#form-and-selector-updates\" class=\"hash-link\" aria-label=\"Direct link to Form and selector updates\" title=\"Direct link to Form and selector updates\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"conditional-ha-form-fields\">Conditional ha-form fields<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#conditional-ha-form-fields\" class=\"hash-link\" aria-label=\"Direct link to Conditional ha-form fields\" title=\"Direct link to Conditional ha-form fields\" translate=\"no\">​</a></h3>\n<p><code>ha-form</code> schemas now support conditional field visibility with <code>visible</code>.</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"advanced\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    selector</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">boolean</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"advanced_name\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    visible</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"> field</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"advanced\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> value</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    selector</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"> text</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><br></div></code></pre></div></div>\n<p>Supported operators are:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"eq\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"not_eq\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"in\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"not_in\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"exists\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"not_exists\"</span><br></div></code></pre></div></div>\n<p>You can also combine conditions with <code>and</code>, <code>or</code>, and <code>not</code>.</p>\n<p>Hidden fields are not rendered and are skipped during validation, so use <code>visible</code> instead of custom frontend-only hiding logic when a form field depends on another value.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"selector-additions\">Selector additions<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#selector-additions\" class=\"hash-link\" aria-label=\"Direct link to Selector additions\" title=\"Direct link to Selector additions\" translate=\"no\">​</a></h3>\n<p>The text selector now supports HTML pattern validation:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  text</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    pattern</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"[a-z0-9_]+\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    validation_message</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Use lowercase letters, numbers, and underscores\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><br></div></code></pre></div></div>\n<p>This works for both single-value and multiple-value text selectors.</p>\n<p>Entity selectors can now filter by properties of the entity's device:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  entity</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    filter</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">      domain</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"sensor\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">      device</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        manufacturer</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Home Assistant\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        model</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"Connect ZBT-1\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><br></div></code></pre></div></div>\n<p>A new <code>ui_clock_date_format</code> selector was also added for the clock card date format editor.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"lovelace-updates\">Lovelace updates<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#lovelace-updates\" class=\"hash-link\" aria-label=\"Direct link to Lovelace updates\" title=\"Direct link to Lovelace updates\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"state_color-is-moving-to-color\">state_color is moving to color<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#state_color-is-moving-to-color\" class=\"hash-link\" aria-label=\"Direct link to state_color is moving to color\" title=\"Direct link to state_color is moving to color\" translate=\"no\">​</a></h3>\n<p>The <code>entities</code> and <code>glance</code> cards now support <code>color</code> as the replacement for <code>state_color</code>.</p>\n<p>Before:</p>\n<div class=\"language-yaml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token key atrule\">type</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> entities</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token key atrule\">state_color</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean important\" style=\"color:rgb(255, 88, 116)\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token key atrule\">entities</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">-</span><span class=\"token plain\"> light.kitchen</span><br></div></code></pre></div></div>\n<p>After:</p>\n<div class=\"language-yaml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token key atrule\">type</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> entities</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token key atrule\">color</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> state</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token key atrule\">entities</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">-</span><span class=\"token plain\"> light.kitchen</span><br></div></code></pre></div></div>\n<p>Use <code>color: state</code> for the old <code>state_color: true</code> behavior, and <code>color: none</code> for <code>state_color: false</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"custom-panels-and-apps\">Custom panels and apps<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#custom-panels-and-apps\" class=\"hash-link\" aria-label=\"Direct link to Custom panels and apps\" title=\"Direct link to Custom panels and apps\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"safe-area-handling\">Safe-area handling<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#safe-area-handling\" class=\"hash-link\" aria-label=\"Direct link to Safe-area handling\" title=\"Direct link to Safe-area handling\" translate=\"no\">​</a></h3>\n<p>Custom panels and add-on app iframes now get safe-area padding by default, so content stays clear of notches, status bars, and home indicators.</p>\n<p>Custom panels that already handle safe areas themselves can opt out:</p>\n<div class=\"language-yaml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token key atrule\">panel_custom</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">-</span><span class=\"token plain\"> </span><span class=\"token key atrule\">name</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> my</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">-</span><span class=\"token plain\">panel</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token key atrule\">module_url</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> /local/my</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">-</span><span class=\"token plain\">panel.js</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token key atrule\">handle_safe_area</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean important\" style=\"color:rgb(255, 88, 116)\">true</span><br></div></code></pre></div></div>\n<p>For iframe-based custom panels, Home Assistant forwards the resolved safe-area values into the iframe document as CSS variables:</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--safe-area-inset-top</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--safe-area-inset-right</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--safe-area-inset-bottom</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token variable\" style=\"color:rgb(191, 199, 213)\">--safe-area-inset-left</span><br></div></code></pre></div></div>\n<p>Add-on app iframes can also opt into managing the safe area themselves when subscribing to Home Assistant properties:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token dom variable\" style=\"color:rgb(191, 199, 213)\">window</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token property-access\">parent</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(130, 170, 255)\">postMessage</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"home-assistant/subscribe-properties\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">handleSafeArea</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">true</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"*\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">;</span><br></div></code></pre></div></div>\n<p>The properties message then includes <code>safeAreaInsets</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"context-and-editor-infrastructure\">Context and editor infrastructure<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#context-and-editor-infrastructure\" class=\"hash-link\" aria-label=\"Direct link to Context and editor infrastructure\" title=\"Direct link to Context and editor infrastructure\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"global-dirty-state\">Global dirty state<a href=\"https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8#global-dirty-state\" class=\"hash-link\" aria-label=\"Direct link to Global dirty state\" title=\"Direct link to Global dirty state\" translate=\"no\">​</a></h3>\n<p><code>DirtyStateProviderMixin</code> now also publishes a global dirty state.</p>\n<p>When any connected dirty-state provider has unsaved changes, <code>window.isDirtyState</code> is set and Home Assistant fires a <code>dirty-state-changed</code> event.</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">window</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">addEventListener</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token string\" style=\"color:rgb(195, 232, 141)\">\"dirty-state-changed\"</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">ev</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">  </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">console</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">log</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">ev</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">detail</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">isDirty</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">;</span><br></div></code></pre></div></div>\n<p>This is useful for shared infrastructure that needs to avoid disrupting active editors or dialogs with unsaved changes.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/07/31/frontend-component-updates-2026.8",
            "title": "Frontend component updates in 2026.8",
            "summary": "Component updates",
            "date_modified": "2026-07-31T00:00:00.000Z",
            "author": {
                "name": "Jan-Philipp Benecke",
                "url": "https://github.com/jpbede"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types",
            "content_html": "<p>Button event entities now have a set of standard event types, provided by the new <code>ButtonEventType</code> enum. Integrations that use <code>EventDeviceClass.BUTTON</code> should use these types instead of custom strings whenever the interaction maps to one of them.</p>\n<p>See the <a href=\"https://github.com/home-assistant/architecture/discussions/1377\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">architecture discussion</a> for the full background.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why\">Why<a href=\"https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types#why\" class=\"hash-link\" aria-label=\"Direct link to Why\" title=\"Direct link to Why\" translate=\"no\">​</a></h2>\n<p>Previously, each integration picked its own strings for button interactions — <code>single</code>, <code>click</code>, <code>hold</code>, <code>double_press</code>, and so on. This inconsistency made it impossible to build generic button automations that work across integrations, and prevented the frontend from offering meaningful trigger suggestions.</p>\n<p>The new <code>ButtonEventType</code> standard event types solve this by giving every button integration a shared vocabulary for the common interactions.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-event-types\">The event types<a href=\"https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types#the-event-types\" class=\"hash-link\" aria-label=\"Direct link to The event types\" title=\"Direct link to The event types\" translate=\"no\">​</a></h2>\n<p><code>ButtonEventType</code> defines six standard event types:</p>\n<ul>\n<li class=\"\"><code>ButtonEventType.PRESS_START</code>: the button was pressed down.</li>\n<li class=\"\"><code>ButtonEventType.PRESS_END</code>: the button was released after a brief press (the standard \"click\").</li>\n<li class=\"\"><code>ButtonEventType.LONG_PRESS_START</code>: the button was held past a duration threshold.</li>\n<li class=\"\"><code>ButtonEventType.LONG_PRESS_END</code>: the button was released after a long hold.</li>\n<li class=\"\"><code>ButtonEventType.MULTI_PRESS_ONGOING</code>: an intermediate press in a multi-press sequence was detected.</li>\n<li class=\"\"><code>ButtonEventType.MULTI_PRESS_END</code>: a multi-press sequence completed.</li>\n</ul>\n<p>The <code>MULTI_PRESS_ONGOING</code> and <code>MULTI_PRESS_END</code> events include a <code>multi_press_count</code> attribute in their event data (the <code>ATTR_MULTI_PRESS_COUNT</code> constant) with the number of presses.</p>\n<p><strong>None of these are mandatory.</strong> Unlike the doorbell <code>ring</code> event, there is no required type here. Each integration maps only the interactions its hardware can actually produce, and lists just those in <code>event_types</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-to-do\">What to do<a href=\"https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types#what-to-do\" class=\"hash-link\" aria-label=\"Direct link to What to do\" title=\"Direct link to What to do\" translate=\"no\">​</a></h2>\n<p>Import <code>ButtonEventType</code> from <code>homeassistant.components.event</code> and include the types your device supports in the entity's <code>event_types</code>. Fire them as the interactions happen:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">components</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">event </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    ATTR_MULTI_PRESS_COUNT</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    ButtonEventType</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    EventDeviceClass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    EventEntity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(255, 203, 107)\">MyButtonEvent</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">EventEntity</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    _attr_device_class </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> EventDeviceClass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">BUTTON</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    _attr_event_types </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        ButtonEventType</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">PRESS_END</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        ButtonEventType</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">LONG_PRESS_END</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        ButtonEventType</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">MULTI_PRESS_END</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token decorator annotation punctuation\" style=\"color:rgb(199, 146, 234)\">@callback</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">_async_handle_multi_press</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> count</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">int</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Handle a completed multi-press sequence.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">_trigger_event</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            ButtonEventType</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">MULTI_PRESS_END</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token plain\">ATTR_MULTI_PRESS_COUNT</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> count</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_write_ha_state</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"single-event-devices\">Single-event devices<a href=\"https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types#single-event-devices\" class=\"hash-link\" aria-label=\"Direct link to Single-event devices\" title=\"Direct link to Single-event devices\" translate=\"no\">​</a></h3>\n<p>If a device only emits a single event per interaction, with no separate press and release, map it to the matching <code>_end</code> type (<code>PRESS_END</code> for short presses, <code>LONG_PRESS_END</code> for holds, and so on). This keeps the \"button was pressed\" trigger consistent across devices without synthesizing events the hardware never sends.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"no-migration-required\">No migration required<a href=\"https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types#no-migration-required\" class=\"hash-link\" aria-label=\"Direct link to No migration required\" title=\"Direct link to No migration required\" translate=\"no\">​</a></h2>\n<p>This change only adds the shared constants; nothing is deprecated and no integration is forced to migrate. Custom event types are still allowed alongside the standard ones. Adopt <code>ButtonEventType</code> when it fits your device.</p>\n<p>For full details, see the <a class=\"\" href=\"https://developers.home-assistant.io/docs/core/entity/event\">event entity documentation</a>.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/07/22/button-standard-event-types",
            "title": "Standard event types for button event entities",
            "summary": "Button event entities now have a set of standard event types, provided by the new ButtonEventType enum. Integrations that use EventDeviceClass.BUTTON should use these types instead of custom strings whenever the interaction maps to one of them.",
            "date_modified": "2026-07-22T00:00:00.000Z",
            "author": {
                "name": "Abílio Costa",
                "url": "https://github.com/abmantis"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary\">Summary<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\" translate=\"no\">​</a></h2>\n<p>A device is now owned by a single config entry, and by a single (or no) config subentry. Devices are no longer merged across integrations: a physical device supported by several integrations is now represented by one device per config entry instead of a single shared device.</p>\n<p>Devices which were previously tied to multiple config entries are split into one device per config entry when the device registry is loaded. The entity registry is updated so entities point to the correct device.</p>\n<p><strong>Most integrations don't interact directly with the device registry and don't need any changes.</strong> Integrations which interact with it directly need to handle the deprecations listed below.</p>\n<p>This is implemented in core <a href=\"https://github.com/home-assistant/core/pull/175785\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #175785</a>, the rationale is described in architecture proposal <a href=\"https://github.com/home-assistant/architecture/discussions/1226\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">home-assistant/architecture#1226</a>. The changes land in Home Assistant Core 2026.8.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"background\">Background<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#background\" class=\"hash-link\" aria-label=\"Direct link to Background\" title=\"Direct link to Background\" translate=\"no\">​</a></h2>\n<p>Until now, a physical device supported by several integrations has been merged into a single, shared device. This was achieved by identifying devices by connections and identifiers which are globally unique, so that for example a device tracker and a native integration referring to the same MAC address end up on the same device.</p>\n<p>This causes a few problems:</p>\n<ul>\n<li class=\"\">There's no single source of truth for device information such as name or model; conflicting values are discarded instead of preserved.</li>\n<li class=\"\">Users get a confusing experience where a device page contains a hodgepodge of entities from multiple integrations.</li>\n<li class=\"\">There are long-standing bugs where modifying the connections and identifiers of a device causes multiple devices to end up with the same connections, violating the original design of the device registry.</li>\n</ul>\n<p>The new behavior is achieved by making identifiers and connections unique per config entry instead of globally unique.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deprecations\">Deprecations<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deprecations\" class=\"hash-link\" aria-label=\"Direct link to Deprecations\" title=\"Direct link to Deprecations\" translate=\"no\">​</a></h2>\n<p>Using the deprecated functionality below logs a warning at runtime. Unless noted otherwise, deprecated functionality remains supported until Home Assistant Core 2027.8.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceentryconfig_entries\"><code>DeviceEntry.config_entries</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceentryconfig_entries\" class=\"hash-link\" aria-label=\"Direct link to deviceentryconfig_entries\" title=\"Direct link to deviceentryconfig_entries\" translate=\"no\">​</a></h3>\n<p>Deprecated, use <code>DeviceEntry.config_entry_id</code> instead. The property is kept as a compatibility shim which returns a set with the device's single config entry.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceentryconfig_entries_subentries\"><code>DeviceEntry.config_entries_subentries</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceentryconfig_entries_subentries\" class=\"hash-link\" aria-label=\"Direct link to deviceentryconfig_entries_subentries\" title=\"Direct link to deviceentryconfig_entries_subentries\" translate=\"no\">​</a></h3>\n<p>Deprecated, use <code>DeviceEntry.config_entry_id</code> and <code>DeviceEntry.config_subentry_id</code> instead. The property is kept as a compatibility shim.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceentryprimary_config_entry\"><code>DeviceEntry.primary_config_entry</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceentryprimary_config_entry\" class=\"hash-link\" aria-label=\"Direct link to deviceentryprimary_config_entry\" title=\"Direct link to deviceentryprimary_config_entry\" translate=\"no\">​</a></h3>\n<p>Deprecated, use <code>DeviceEntry.config_entry_id</code> instead. A device now belongs to a single config entry, which is its primary config entry.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"reading-config-entries-of-a-composite-device\">Reading config entries of a composite device<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#reading-config-entries-of-a-composite-device\" class=\"hash-link\" aria-label=\"Direct link to Reading config entries of a composite device\" title=\"Direct link to Reading config entries of a composite device\" translate=\"no\">​</a></h3>\n<p><code>DeviceEntry.config_entries</code>, <code>DeviceEntry.config_entries_subentries</code> and <code>DeviceEntry.primary_config_entry</code> are only deprecated for ordinary devices, which belong to a single config entry. They are not deprecated when interacting with a synthesized composite device, the read-only device the backwards compatibility resolution returns for a pre-migration composite device id (see <a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#backwards-compatibility\" class=\"\">Backwards compatibility</a>). Such a device spans several config entries, which <code>config_entry_id</code> and <code>config_subentry_id</code> can't represent, so these three properties, which report the union across the split devices, remain the way to read that information.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceinfovia_device-and-deviceregistryasync_get_or_createvia_device\"><code>DeviceInfo[\"via_device\"]</code> and <code>DeviceRegistry.async_get_or_create(via_device=...)</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceinfovia_device-and-deviceregistryasync_get_or_createvia_device\" class=\"hash-link\" aria-label=\"Direct link to deviceinfovia_device-and-deviceregistryasync_get_or_createvia_device\" title=\"Direct link to deviceinfovia_device-and-deviceregistryasync_get_or_createvia_device\" translate=\"no\">​</a></h3>\n<p>Deprecated, use <code>via_device_id</code> instead. Because identifiers are only unique per config entry, an identifier pair no longer unambiguously points at a single device, which is why <code>via_device</code> is deprecated.</p>\n<p>Passing both <code>via_device</code> and <code>via_device_id</code> raises <code>HomeAssistantError</code>.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceregistryasync_update_device-config-entry-parameters\"><code>DeviceRegistry.async_update_device()</code> config entry parameters<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceregistryasync_update_device-config-entry-parameters\" class=\"hash-link\" aria-label=\"Direct link to deviceregistryasync_update_device-config-entry-parameters\" title=\"Direct link to deviceregistryasync_update_device-config-entry-parameters\" translate=\"no\">​</a></h3>\n<p>The <code>add_config_entry_id</code>, <code>add_config_subentry_id</code>, <code>remove_config_entry_id</code> and <code>remove_config_subentry_id</code> parameters are all deprecated. A device belongs to a single config entry and subentry, so adding and removing config entries is no longer meaningful; a device is instead moved or removed.</p>\n<p>To move a device to another config entry or subentry, pass the new <code>new_config_entry_id</code> and <code>new_config_subentry_id</code> parameters:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device_registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_update_device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    new_config_entry_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">config_entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    new_config_subentry_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">subentry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">subentry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Moving a device with the old parameters took the integration several <code>async_update_device</code> calls, adding the device to the new config entry and subentry and then removing it from the old ones, with a separate case for a device that only changed subentry within the same config entry. The single call shown above replaces all of that. In addition, the device registry now clears a <code>CONFIG_ENTRY</code> disable when a device is moved to an enabled config entry, so the integration no longer has to carry the <code>disabled_by</code> flag across the move by hand.</p>\n<p>Relatedly, <code>async_update_device</code> now validates the <code>disabled_by</code> flag against the owning config entry's disabled state. Setting <code>disabled_by=None</code> for a device on a disabled config entry, or <code>disabled_by=DeviceEntryDisabler.CONFIG_ENTRY</code> for a device on an enabled config entry, is inconsistent; such a value is ignored and logged now, and will raise from Home Assistant Core 2027.8.</p>\n<p>Core integrations have been updated as examples: <code>openai_conversation</code> in <a href=\"https://github.com/home-assistant/core/pull/176662\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176662</a>, <code>scrape</code> in <a href=\"https://github.com/home-assistant/core/pull/176663\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176663</a>, <code>waqi</code> in <a href=\"https://github.com/home-assistant/core/pull/176664\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176664</a> and <code>wolflink</code> in <a href=\"https://github.com/home-assistant/core/pull/176665\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176665</a>.</p>\n<p>To remove a device, call <code>DeviceRegistry.async_remove_device()</code>:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device_registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_remove_device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Core integrations have been updated to remove devices this way in PRs <a href=\"https://github.com/home-assistant/core/pull/176669\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">#176669</a>, <a href=\"https://github.com/home-assistant/core/pull/176671\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">#176671</a>, <a href=\"https://github.com/home-assistant/core/pull/176672\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">#176672</a> and <a href=\"https://github.com/home-assistant/core/pull/176673\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">#176673</a>.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deviceregistryasync_get_device\"><code>DeviceRegistry.async_get_device()</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#deviceregistryasync_get_device\" class=\"hash-link\" aria-label=\"Direct link to deviceregistryasync_get_device\" title=\"Direct link to deviceregistryasync_get_device\" translate=\"no\">​</a></h3>\n<p>Deprecated. Identifiers and connections are only unique per config entry, so a lookup by identifiers or connections can by design match more than one device, and what <code>async_get_device</code> returns is therefore ambiguous.</p>\n<p>When the owning config entry is known, look the device up scoped to that config entry with the new methods <code>DeviceRegistry.async_get_device_by_identifier()</code> or <code>DeviceRegistry.async_get_device_by_connection()</code>. Each takes a single identifier or connection tuple plus the config entry id, so the lookup can no longer be ambiguous:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># Before</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device_registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">identifiers</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">{</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> serial_number</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">}</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># After</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">device </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> device_registry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">async_get_device_by_identifier</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">DOMAIN</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> serial_number</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>Inside an entity, prefer <code>self.device_entry</code> over a registry lookup. If you genuinely need every device matching a key, possibly across config entries, use <code>DeviceRegistry.async_get_devices()</code>, which returns a list.</p>\n<p>Core integrations are migrated to the new methods, <code>heos</code> in core <a href=\"https://github.com/home-assistant/core/pull/176932\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176932</a> is an example.</p>\n<p>During the deprecation period, <code>async_get_device</code> resolves an ambiguous lookup as described in <a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#backwards-compatibility\" class=\"\">Backwards compatibility</a> below. Note that this backwards-compatible resolution only happens through the <code>DeviceRegistry</code> lookup methods such as <code>async_get()</code> and <code>async_get_device()</code>; interacting with the <code>devices</code> container directly, for example <code>DeviceRegistry.devices.get(device_id)</code>, does not synthesize a composite device.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"adding-a-helper-config-entry-to-another-integrations-device\">Adding a helper config entry to another integration's device<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#adding-a-helper-config-entry-to-another-integrations-device\" class=\"hash-link\" aria-label=\"Direct link to Adding a helper config entry to another integration's device\" title=\"Direct link to Adding a helper config entry to another integration's device\" translate=\"no\">​</a></h3>\n<p>Helper integrations must not add their config entry to the source entity's device or to a user-selected device, they should link their entities to the device instead. This is a direct consequence of the change described here: a device now belongs to a single config entry, so a helper config entry can no longer be added to a device owned by another integration.</p>\n<p>This was announced last year in <a class=\"\" href=\"https://developers.home-assistant.io/blog/2025/07/18/updated-pattern-for-helpers-linking-to-devices\">Updated guidelines for helper integrations linking to other integration's device</a>, and stops working in Home Assistant Core 2026.8.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"helpersdeviceasync_device_info_to_link_from_entity-and-async_device_info_to_link_from_device_id\"><code>helpers.device.async_device_info_to_link_from_entity()</code> and <code>async_device_info_to_link_from_device_id()</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#helpersdeviceasync_device_info_to_link_from_entity-and-async_device_info_to_link_from_device_id\" class=\"hash-link\" aria-label=\"Direct link to helpersdeviceasync_device_info_to_link_from_entity-and-async_device_info_to_link_from_device_id\" title=\"Direct link to helpersdeviceasync_device_info_to_link_from_entity-and-async_device_info_to_link_from_device_id\" translate=\"no\">​</a></h3>\n<p>Both helpers now always return <code>None</code>.</p>\n<p>They returned a <code>DeviceInfo</code> carrying another device's identifiers and connections, which implicitly added the caller's config entry to that device. A device with a single config entry can't represent that, it would silently fork a duplicate device instead.</p>\n<p>Link the helper entity to the device by setting <code>self.device_entry</code> in the entity's constructor instead:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">device_entry </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> async_entity_id_to_device</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> source_entity_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>The helpers are removed in Home Assistant Core 2027.8.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"helpershelper_integrationasync_handle_source_entity_changesadd_helper_config_entry_to_device\"><code>helpers.helper_integration.async_handle_source_entity_changes(add_helper_config_entry_to_device=...)</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#helpershelper_integrationasync_handle_source_entity_changesadd_helper_config_entry_to_device\" class=\"hash-link\" aria-label=\"Direct link to helpershelper_integrationasync_handle_source_entity_changesadd_helper_config_entry_to_device\" title=\"Direct link to helpershelper_integrationasync_handle_source_entity_changesadd_helper_config_entry_to_device\" translate=\"no\">​</a></h3>\n<p>The parameter no longer has any effect and should be removed from the call.</p>\n<p>When the source entity moves to another device, <code>async_handle_source_entity_changes</code> now only updates the helper entity to link to the new device, it no longer removes the helper config entry from the old device and adds it to the new one.</p>\n<p>Passing the parameter is accepted until Home Assistant Core 2027.8, and logs a warning.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"cleaning-up-helper-devices\">Cleaning up helper devices<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#cleaning-up-helper-devices\" class=\"hash-link\" aria-label=\"Direct link to Cleaning up helper devices\" title=\"Direct link to Cleaning up helper devices\" translate=\"no\">​</a></h3>\n<p>The helper used to clean up a helper integration's devices from a config entry migration step has been renamed from <code>async_remove_helper_config_entry_from_source_device</code> to <code>homeassistant.helpers.helper_integration.async_remove_helper_devices</code> in core <a href=\"https://github.com/home-assistant/core/pull/176714\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176714</a>. The old name is kept as a deprecated alias which keeps working until Home Assistant Core 2027.8. The new signature is:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_remove_helper_devices</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> HomeAssistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">*</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    helper_config_entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    source_device_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">|</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    remove_all_devices</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">bool</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">False</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    keep_device_ids</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> Collection</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token builtin\" style=\"color:rgb(130, 170, 255)\">str</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">None</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><br></div></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"helpersdeviceasync_remove_stale_devices_links_keep_entity_device-and-async_remove_stale_devices_links_keep_current_device\"><code>helpers.device.async_remove_stale_devices_links_keep_entity_device()</code> and <code>async_remove_stale_devices_links_keep_current_device()</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#helpersdeviceasync_remove_stale_devices_links_keep_entity_device-and-async_remove_stale_devices_links_keep_current_device\" class=\"hash-link\" aria-label=\"Direct link to helpersdeviceasync_remove_stale_devices_links_keep_entity_device-and-async_remove_stale_devices_links_keep_current_device\" title=\"Direct link to helpersdeviceasync_remove_stale_devices_links_keep_entity_device-and-async_remove_stale_devices_links_keep_current_device\" translate=\"no\">​</a></h3>\n<p>Both are now no-ops. Call <code>async_remove_helper_devices</code> with <code>remove_all_devices=True</code> from the helper's <code>async_setup_entry</code> instead:</p>\n<div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">async_remove_helper_devices</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    hass</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    helper_config_entry_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">entry_id</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    source_device_id</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">entry</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">options</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">get</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">CONF_DEVICE_ID</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    remove_all_devices</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token boolean\" style=\"color:rgb(255, 88, 116)\">True</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>The <code>template</code> helper has been migrated as an example in core <a href=\"https://github.com/home-assistant/core/pull/176900\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176900</a>. The functions are removed in Home Assistant Core 2027.8.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"devices-can-only-have-a-single-config-subentry\">Devices can only have a single config subentry<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#devices-can-only-have-a-single-config-subentry\" class=\"hash-link\" aria-label=\"Direct link to Devices can only have a single config subentry\" title=\"Direct link to Devices can only have a single config subentry\" translate=\"no\">​</a></h2>\n<p>A device can no longer be tied to more than one config subentry. This is a breaking change without a backwards compatibility shim; integrations which attach several subentries to the same device must create one device per subentry.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example-telegram_bot\">Example: <code>telegram_bot</code><a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#example-telegram_bot\" class=\"hash-link\" aria-label=\"Direct link to example-telegram_bot\" title=\"Direct link to example-telegram_bot\" translate=\"no\">​</a></h3>\n<p>The <code>telegram_bot</code> integration has been adjusted for this in core <a href=\"https://github.com/home-assistant/core/pull/176606\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PR #176606</a>, which can be used as an example.</p>\n<p>It previously had a single bot device shared by every chat, with each chat's subentry attached to that same device. It now creates an individual device per chat, linked to the bot device as a via device. A config entry migration moves each chat's notify entity onto its own device and strips the chat subentries from the bot device, leaving the bot device with no subentry.</p>\n<p>Note that the PR was written before <code>via_device_id</code> was added, new code should use <code>via_device_id</code> instead of <code>via_device</code>.</p>\n<p>When <a href=\"https://github.com/home-assistant/architecture/discussions/1414\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">child devices</a> are introduced, integrations which model this with a via device should migrate to child devices instead.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"linking-an-entity-to-a-split-device\">Linking an entity to a split device<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#linking-an-entity-to-a-split-device\" class=\"hash-link\" aria-label=\"Direct link to Linking an entity to a split device\" title=\"Direct link to Linking an entity to a split device\" translate=\"no\">​</a></h2>\n<p>A pre-migration composite device id no longer refers to a real device. Attempting to link an entity to such an id, by passing it to <code>EntityRegistry.async_get_or_create(device_id=...)</code> or <code>EntityRegistry.async_update_entity(device_id=...)</code>, is ignored with a logged warning rather than applied. A new entity is then created with no device, and an existing entity keeps its current device. Passing a genuinely non-existent device id still raises <code>ValueError</code> as before.</p>\n<p>Entities whose stored device is a composite device with no split owned by the entity's config entry are detached from the device when the registry is loaded; the owning integration is expected to re-link them.</p>\n<p>Link entities to one of the split devices instead, looking it up with <code>async_get_device_by_identifier</code> or <code>async_get_device_by_connection</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"device-registry-events\">Device registry events<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#device-registry-events\" class=\"hash-link\" aria-label=\"Direct link to Device registry events\" title=\"Direct link to Device registry events\" translate=\"no\">​</a></h2>\n<p>Splitting a pre-migration composite device happens when the registry is loaded from storage, before any listeners run, so it emits no <code>EVENT_DEVICE_REGISTRY_UPDATED</code> events; devices are already split at startup.</p>\n<p>Two things change for integrations which subscribe to <code>EVENT_DEVICE_REGISTRY_UPDATED</code>, or use <code>async_track_device_registry_updated_event</code>, and inspect the payload:</p>\n<ul>\n<li class=\"\">The <code>changes</code> dict of an <code>update</code> event reports a device move with the keys <code>config_entry_id</code> and <code>config_subentry_id</code>, replacing the previous <code>config_entries</code> and <code>config_entries_subentries</code>.</li>\n<li class=\"\">Updating or removing a pre-migration composite device id forwards the operation to each split device, so one event is fired per split device rather than a single event for the composite id.</li>\n</ul>\n<p>A device now belongs to a single config entry, so it can no longer lose one config entry while staying around for another. Integrations which previously watched <code>update</code> events for a change to the <code>config_entries</code> or <code>config_entries_subentries</code> keys, typically to detect their config entry being removed from a device shared with another integration, probably only need to handle <code>remove</code> events now: a device losing its config entry means the device is removed.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"backwards-compatibility\">Backwards compatibility<a href=\"https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry#backwards-compatibility\" class=\"hash-link\" aria-label=\"Direct link to Backwards compatibility\" title=\"Direct link to Backwards compatibility\" translate=\"no\">​</a></h2>\n<p>Splitting devices changes assumptions which custom integrations may rely on, and device ids which are stored in automations and scripts no longer exist as devices. To soften that, the device registry makes a best-effort attempt to keep unmodified custom integrations working, by resolving a pre-migration composite device id to the devices it was split into.</p>\n<p>This is best-effort, not a guarantee. The shims can't cover every way a custom integration interacts with the device registry, and an operation which is ambiguous across the split devices can't be applied at all. An AI-assisted analysis of 462 custom integrations interacting directly with the device registry suggests at least 90% are expected to work unaffected, which also means some will not. Please migrate your integration to the new API rather than relying on these shims; they are removed in Home Assistant Core 2027.8.</p>\n<p>During the deprecation period:</p>\n<ul>\n<li class=\"\"><code>DeviceRegistry.async_get()</code> synthesizes a read-only restored composite device when passed the id of a pre-migration composite device. Its identifiers, connections and config entries are the union of the split devices'. The synthesis only happens in <code>async_get()</code>; interacting with the <code>devices</code> container directly, for example <code>DeviceRegistry.devices.get(device_id)</code>, does not synthesize a composite and returns <code>None</code> for a pre-migration composite device id.</li>\n<li class=\"\"><code>DeviceRegistry.async_get_device()</code> resolves a lookup by identifiers or connections matching several config entries to a single device when possible, preferring the device whose config entry domain matches the looked-up identifier. If the remaining matches are the splits of one pre-migration composite device, a read-only composite spanning them is returned. For independent devices sharing an identifier or connection, a device owned by the calling integration is preferred, falling back to the first match.</li>\n<li class=\"\"><code>DeviceRegistry.async_update_device()</code> and <code>DeviceRegistry.async_remove_device()</code> forward the call to each of the split devices. Arguments which rewrite a device's identity or move it are ambiguous across the split devices; they are ignored and reported to the offending integration.</li>\n<li class=\"\">Entity registry <code>get_entries_for_device_id()</code> and <code>async_entries_for_device()</code> expand a pre-migration composite device id to the entities of the devices it was split into.</li>\n<li class=\"\">Actions targeting a pre-migration composite device id trickle down to the split devices.</li>\n<li class=\"\">User customizations (area, floor, labels, name) are kept when a device is split.</li>\n</ul>\n<p>A new method <code>DeviceRegistry.async_get_devices_for_composite_device_id()</code> returns the devices a pre-migration composite device was split into. <code>DeviceRegistry.async_is_composite_device_id()</code> returns whether a device id is a pre-migration composite device id, that is, an id which was split into one device per config entry and no longer refers to a registered device.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/07/21/device-registry-single-config-entry",
            "title": "Devices are restricted to a single config entry and at most one subentry",
            "summary": "Summary",
            "date_modified": "2026-07-21T00:00:00.000Z",
            "author": {
                "name": "Erik Montnemery",
                "url": "https://github.com/emontnemery"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/20/ai-policy",
            "content_html": "<p>AI-assisted development has become part of daily reality for many contributors, and for us as maintainers too. That can be a good thing: AI tools help people write code, improve their English, and find their way in an unfamiliar codebase. It also has a downside: unreviewed AI output submitted as a contribution costs maintainers real time, and maintainer time is the scarcest resource an open source project has.</p>\n<p>Today, we are publishing the <a class=\"\" href=\"https://developers.home-assistant.io/docs/ai_policy\">Open Home Foundation AI Policy</a>. It applies to all Open Home Foundation projects, including every repository in the <code>home-assistant</code> and <code>home-assistant-libs</code> GitHub organizations.</p>\n<p>The policy boils down to a few points:</p>\n<ul>\n<li class=\"\">AI tools are welcome as an aid. You remain responsible for everything you submit.</li>\n<li class=\"\">Autonomous agents are not allowed to contribute. Pull requests and issues that were created autonomously will be closed.</li>\n<li class=\"\">You must understand and be able to explain every change you submit, in your own words. This includes answering questions from maintainers yourself, not having an AI do it for you.</li>\n<li class=\"\">Using AI to improve the grammar or clarity of text you have written yourself is fine. For non-native English speakers this is genuinely useful, and we appreciate the effort.</li>\n</ul>\n<p>We are rolling the policy out to every repository in both organizations. Each repository gets an <code>AI_POLICY.md</code> file in its root, and existing contributing guidelines and <code>AGENTS.md</code> files gain a reference to it, so both humans and their AI tools know what we expect.</p>\n<p>Read the full policy in the <a class=\"\" href=\"https://developers.home-assistant.io/docs/ai_policy\">developer documentation</a>. If a contribution does not follow it, it will be closed. If you believe that happened to yours in error, reach out to a maintainer and we will sort it out.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/07/20/ai-policy",
            "title": "Introducing the Open Home Foundation AI Policy",
            "summary": "AI-assisted development has become part of daily reality for many contributors, and for us as maintainers too. That can be a good thing unreviewed AI output submitted as a contribution costs maintainers real time, and maintainer time is the scarcest resource an open source project has.",
            "date_modified": "2026-07-20T00:00:00.000Z",
            "author": {
                "name": "Franck Nijhof",
                "url": "https://github.com/frenck"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus",
            "content_html": "<p>Modbus is everywhere in the modern home: solar inverters, energy meters, heat pumps, and all kinds of industrial equipment that has found its way indoors. Home Assistant has long supported these devices through the YAML-based <code>modbus</code> integration, where users hand-write register maps in their configuration. That integration is not going anywhere, and existing setups keep working. But hand-writing register maps puts the burden of understanding a device's protocol on every user, and it does not fit the config-flow, UI-first direction the rest of Home Assistant has taken.</p>\n<p>So we are adding a new way to use Modbus: an integration-based approach, where a device integration owns the device-specific knowledge and the user simply picks their device in the UI, the same as any other integration.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"sharing-a-connection\">Sharing a connection<a href=\"https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus#sharing-a-connection\" class=\"hash-link\" aria-label=\"Direct link to Sharing a connection\" title=\"Direct link to Sharing a connection\" translate=\"no\">​</a></h2>\n<p>A Modbus connection is a single, exclusive resource: only one party can talk on the bus at a time. A serial (RS-485) bus, or a TCP-to-serial gateway, can carry many devices at once, sometimes from different manufacturers. If two integrations each open their own connection to the same bus, they fight over it, and historically Home Assistant did not support sharing a bus between integrations at all.</p>\n<p>The <code>modbus</code> integration solves this by handing out units over connections it shares. A device integration collects its own connection details in its own config flow, the same as any other integration, and asks <code>modbus</code> for a unit on them. Two integrations that ask with equal details get units over one connection, so their requests serialize behind it instead of contending for the bus.</p>\n<p>The shared connection is not configured or persisted separately. It exists only while an integration holds a unit, and it closes when the last consumer's config entry unloads. That keeps the user experience where it belongs — you set up your heat pump, not a bus — while still giving the bus a single owner. The <a class=\"\" href=\"https://developers.home-assistant.io/docs/modbus/introduction\">Modbus developer documentation</a> covers how to ask for a unit, with example code.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"a-standalone-library\">A standalone library<a href=\"https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus#a-standalone-library\" class=\"hash-link\" aria-label=\"Direct link to A standalone library\" title=\"Direct link to A standalone library\" translate=\"no\">​</a></h2>\n<p>The connection abstraction underneath <code>modbus</code> lives in <a href=\"https://home-assistant-libs.github.io/modbus-connection/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>modbus-connection</code></a>, a new library we designed for this purpose and published on PyPI. It is not bound to Home Assistant and can be used standalone in any Python project. It presents a common, backend-neutral interface, so device library authors write against one API regardless of the underlying Modbus implementation, and it ships a device-modelling framework and a <code>pytest</code> plugin to make building and testing a device library straightforward.</p>\n<p>This keeps concerns where they belong. A device library is a normal PyPI package that knows how to talk to a specific device, and a consuming integration in Home Assistant wires that library up to a shared connection and exposes entities. Both can be developed and tested independently.</p>\n<p>For more background, see our <a href=\"https://gist.github.com/balloob/b9fa91ba1a0914a9787f8f6ceb637b83\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">research</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"lets-get-building\">Let's get building<a href=\"https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus#lets-get-building\" class=\"hash-link\" aria-label=\"Direct link to Let's get building\" title=\"Direct link to Let's get building\" translate=\"no\">​</a></h2>\n<p>With these new building blocks in place, it is now possible to turn a collection of YAML configuration for Modbus into manufacturer-specific integrations that people can set up via the UI. If you're (interested in) working on this, stop by the <a href=\"https://discord.com/channels/330944238910963714/1347329854495916044\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">#modbus channel on the Home Assistant Discord</a> and we'll be happy to help.</p>\n<p>If you're using an AI agent, you can give it the following prompt:</p>\n<blockquote>\n<p>I want to create a new integration for Home Assistant using the new Modbus Connection integration as documented here: <a href=\"https://developers.home-assistant.io/docs/modbus/introduction\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://developers.home-assistant.io/docs/modbus/introduction</a></p>\n<p>The YAML we want to turn into a device library can be found here: <em>TODO INSERT LOCATION OF MODBUS YAML!</em></p>\n<p>The deliverables of this task are going to be 3 folders:</p>\n<ul>\n<li class=\"\">\n<p>Start by creating a device library based on the YAML. Follow <a href=\"https://github.com/darkrain-nl/sofar-modbus\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://github.com/darkrain-nl/sofar-modbus</a> as an exact example, including how to use component models, all GitHub Actions, helper scripts and README. This library is meant as a standalone device library and should not mention Home Assistant. Look at the source of modbus_connection to figure out all supported fields.</p>\n</li>\n<li class=\"\">\n<p>Create an integration that can be contributed to Home Assistant core that follows this example: <a href=\"https://github.com/home-assistant/core/tree/dev/homeassistant/components/sofar\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://github.com/home-assistant/core/tree/dev/homeassistant/components/sofar</a></p>\n</li>\n<li class=\"\">\n<p>Create a custom integration version where the device library is vendorized so it is ready to be tested by the community via HACS. Follow this template <a href=\"https://github.com/ludeeus/integration_blueprint\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://github.com/ludeeus/integration_blueprint</a></p>\n</li>\n</ul>\n</blockquote>",
            "url": "https://developers.home-assistant.io/blog/2026/07/05/modernizing-modbus",
            "title": "Modernizing Modbus in Home Assistant",
            "summary": "Modbus is everywhere in the modern home: solar inverters, energy meters, heat pumps, and all kinds of industrial equipment that has found its way indoors. Home Assistant has long supported these devices through the YAML-based modbus integration, where users hand-write register maps in their configuration. That integration is not going anywhere, and existing setups keep working. But hand-writing register maps puts the burden of understanding a device's protocol on every user, and it does not fit the config-flow, UI-first direction the rest of Home Assistant has taken.",
            "date_modified": "2026-07-05T00:00:00.000Z",
            "author": {
                "name": "Paulus Schoutsen",
                "url": "https://github.com/balloob"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/07/03/media-source-search",
            "content_html": "<p>Media sources can now implement search. By adding an <code>async_search_media</code> method to your <code>MediaSource</code>, users can search through your media directly from the media browser.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#bfc7d5;--prism-background-color:#292d3e\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#bfc7d5;background-color:#292d3e\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token keyword\" style=\"font-style:italic\">from</span><span class=\"token plain\"> homeassistant</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">components</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token plain\">media_player </span><span class=\"token keyword\" style=\"font-style:italic\">import</span><span class=\"token plain\"> SearchMedia</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> SearchMediaQuery</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"font-style:italic\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"font-style:italic\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(130, 170, 255)\">async_search_media</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    self</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> item</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> MediaSourceItem</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"> SearchMediaQuery</span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">-</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">&gt;</span><span class=\"token plain\"> SearchMedia</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(195, 232, 141)\">\"\"\"Search media.\"\"\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    results </span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">[</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">.</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">]</span><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(105, 112, 152);font-style:italic\"># list of BrowseMediaSource items</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#bfc7d5\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"font-style:italic\">return</span><span class=\"token plain\"> SearchMedia</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">(</span><span class=\"token plain\">result</span><span class=\"token operator\" style=\"color:rgb(137, 221, 255)\">=</span><span class=\"token plain\">results</span><span class=\"token punctuation\" style=\"color:rgb(199, 146, 234)\">)</span><br></div></code></pre></div></div>\n<p>To tell the media browser which items can be searched, set the <code>can_search</code> flag to <code>True</code> on the <code>BrowseMediaSource</code> items you return while browsing (typically directories). Other integrations can trigger a search through the new <code>media_source.async_search_media</code> helper.</p>\n<p>For more info, see <a class=\"\" href=\"https://developers.home-assistant.io/docs/core/platform/media_source#searching-media\">the updated documentation.</a></p>",
            "url": "https://developers.home-assistant.io/blog/2026/07/03/media-source-search",
            "title": "Media sources can now be searched",
            "summary": "Media sources can now implement search. By adding an asyncsearchmedia method to your MediaSource, users can search through your media directly from the media browser.",
            "date_modified": "2026-07-03T00:00:00.000Z",
            "author": {
                "name": "Josef Zweck",
                "url": "https://github.com/zweckj"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/06/30/async-initialize-triggers-home-assistant-start-deprecated",
            "content_html": "<p>The dedicated <code>home_assistant_start</code> flag of <code>async_initialize_triggers</code> is deprecated and will be removed in Home Assistant Core 2027.8. During the deprecation period the parameter no longer has any effect.</p>\n<p>The flag existed because the <code>homeassistant</code> start trigger was a pseudo trigger: instead of acting like a real trigger, it relied on the caller of the trigger API passing <code>home_assistant_start=True</code> so that <code>async_initialize_triggers</code> would fire the trigger during startup.</p>\n<p>The start trigger has been rewritten to work as a real trigger, so the flag is no longer needed. Callers of <code>async_initialize_triggers</code> should simply stop passing <code>home_assistant_start</code>.</p>\n<p>To make the new implementation possible, the rewrite adds <code>HomeAssistant.async_add_startup_job</code>, which registers a job to be called after all listeners to <code>EVENT_HOMEASSISTANT_START</code> have executed, but before <code>EVENT_HOMEASSISTANT_STARTED</code> is fired. This mirrors the approach already used for the homeassistant shutdown trigger, and avoids adding yet another core state and event to the already complex relationship between core states and events.</p>\n<p>For more details, see <a href=\"https://github.com/home-assistant/core/pull/175160\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">core PR 175160</a>.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/06/30/async-initialize-triggers-home-assistant-start-deprecated",
            "title": "Deprecation of the home_assistant_start flag of async_initialize_triggers",
            "summary": "The dedicated homeassistantstart flag of asyncinitializetriggers is deprecated and will be removed in Home Assistant Core 2027.8. During the deprecation period the parameter no longer has any effect.",
            "date_modified": "2026-06-30T00:00:00.000Z",
            "author": {
                "name": "Erik Montnemery",
                "url": "https://github.com/emontnemery"
            },
            "tags": []
        },
        {
            "id": "https://developers.home-assistant.io/blog/2026/06/30/new-unit-enumerators",
            "content_html": "<p>As of Home Assistant Core 2026.7, the following unit constants are deprecated and replaced\nby a corresponding enum:</p>\n<ul>\n<li class=\"\"><code>UnitOfDensity</code> enumerator replaces mass over volume <code>CONCENTRATION_***</code> constants\n(<code>\"g/m³\"</code>, <code>\"mg/m³\"</code>, <code>\"μg/m³\"</code>, <code>\"μg/ft³\"</code>)</li>\n<li class=\"\"><code>UnitOfRatio</code> enumerator replaces unit-less ratio <code>CONCENTRATION_***</code> constants\n(<code>\"ppm\"</code>, <code>\"ppb\"</code>)</li>\n</ul>\n<p><code>CONCENTRATION_PARTS_PER_CUBIC_METER</code> was only used by a single integration and is deprecated\nwithout a replacement unit.</p>\n<p>Please note that the use of <code>PERCENTAGE</code> constant is also deprecated when used as a unit of\nmeasurement, even if the constant itself is not deprecated.</p>",
            "url": "https://developers.home-assistant.io/blog/2026/06/30/new-unit-enumerators",
            "title": "Introducing new unit enumerators",
            "summary": "As of Home Assistant Core 2026.7, the following unit constants are deprecated and replaced",
            "date_modified": "2026-06-30T00:00:00.000Z",
            "author": {
                "name": "epenet",
                "url": "https://github.com/epenet"
            },
            "tags": []
        }
    ]
}