Control a whole area by name - #5685
Conversation
Home Assistant answers a service call as soon as it has passed it on, not once the device has reported back, so reading the state straight afterwards returned the state the action replaced: a light just switched on drew as off. Read it back until it shows the command landed, then draw whatever the last read said, so a device that refuses still reports the truth. A cover holds opening or closing for longer than any card will wait, and that already proves the command landed, so it counts. Also brings the card's type down a step: it is drawn at Siri's scale, where the sizes it used ran large enough to wrap the area line.
"Turn on the living room lights" had nothing to match: a command could only ever name one entity, so a room full of them had to be asked for one at a time. Each command now offers the areas holding something it can act on, next to its individual entities, and targets the area itself. Home Assistant scopes an area by the calling service's domain, so turning on a room's lights leaves its television alone. The targets are named for what they hold, down to the device class where a room agrees on one, so a room of curtains reads as curtains rather than as covers. Matching takes the area's aliases too, which is what people set them for. No new shortcuts: the commands that already exist gained the rows.
|
Found 2 unused localization strings in the codebase. Click to see detailsTo remove them, run the |
# Conflicts: # Sources/Extensions/AppIntents/Control/ControlEntityIntentRunner+Snippet.swift # Sources/Extensions/AppIntents/Control/OpenCloseEntityAppIntent.swift
Both suites mapped every row to its entity id, which an area row leaves empty. They now check the entities and the areas apart, which also pins that a light and a switch sharing a room contribute no area target to the open and close command.
There was a problem hiding this comment.
🟡 Changes recommended
sharedDeviceClass can misclassify mixed/nil device classes (leading to incorrect area naming), which should be corrected before merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds support for “whole area” targets (e.g., “Living room lights”) to existing App Intents control commands so Siri/Shortcuts can match and act on an area-by-domain target in addition to individual entities.
Changes:
- Introduces
AreaTarget+AreaTargetProviderand surfaces area-level rows in controllable/openable entity pickers and identifier resolution. - Updates control intent execution to target
area_idwhen appropriate and suppress result cards for area actions (no single state to show). - Adds localization keys and unit tests covering area target building, naming (including cover device classes), matching, and ID stability.
File summaries
| File | Description |
|---|---|
| Tests/Shared/AreaTarget.test.swift | New unit tests for area target construction, naming/matching, and ID behavior. |
| Tests/App/Utilities/OpenableEntityAppEntityQueryTests.swift | Updates open/close entity query tests to account for added area target rows. |
| Tests/App/Utilities/ControllableEntityAppEntityQueryTests.swift | Updates on/off entity query tests to account for added area target rows. |
| Sources/Shared/Resources/Swiftgen/Strings.swift | Adds SwiftGen accessors for new app_intents.area_target.* strings. |
| Sources/Shared/Domain/AreaTargetProvider.swift | New provider to build area targets from Siri-exposed entities + areas. |
| Sources/Shared/Domain/AreaTarget.swift | New AreaTarget model plus helpers to build targets and compute shared device class. |
| Sources/Extensions/AppIntents/Control/OpenCloseEntityAppIntent.swift | Allows open/close to target an area and omits snippet for area actions. |
| Sources/Extensions/AppIntents/Control/OpenableEntityAppEntityQuery.swift | Adds area target rows/sections and resolves saved area IDs. |
| Sources/Extensions/AppIntents/Control/OpenableEntityAppEntity.swift | Adds areaTarget support, subtitle behavior, and service targeting via area_id. |
| Sources/Extensions/AppIntents/Control/ControllableEntityAppEntityQuery.swift | Adds area target rows/sections and resolves saved area IDs. |
| Sources/Extensions/AppIntents/Control/ControllableEntityAppEntity.swift | Adds areaTarget support, subtitle behavior, and service targeting via area_id. |
| Sources/Extensions/AppIntents/Control/ControlEntityIntentRunner+Snippet.swift | Suppresses result card for area actions (dialog only). |
| Sources/Extensions/AppIntents/Control/ControlEntityIntentRunner.swift | Uses serviceTarget for calls and adjusts toggle behavior for area targets. |
| Sources/App/Resources/en.lproj/Localizable.strings | Adds English strings for area-target naming and section header. |
| HomeAssistant.xcodeproj/project.pbxproj | Adds the new shared test file to the unit test target build phase. |
Review details
- Files reviewed: 15/15 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var sharedDeviceClass: DeviceClass? { | ||
| let classes = Set(compactMap(\.rawDeviceClass)) | ||
| guard classes.count == 1, let raw = classes.first else { return nil } | ||
| return DeviceClass(rawValue: raw) | ||
| } | ||
| } |
| if entity.areaTarget != nil { | ||
| return domain.toggleServices.map { _ in Service.toggle } ?? services.off | ||
| } |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5685 +/- ##
==========================================
+ Coverage 39.06% 39.35% +0.29%
==========================================
Files 1074 1076 +2
Lines 75445 75895 +450
==========================================
+ Hits 29474 29871 +397
- Misses 45971 46024 +53
☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
AI Policy
Select exactly one option that describes AI usage in this contribution:
Summary
"Turn on the living room lights" had nothing to match. A spoken command could only ever name a single entity, so a room full of them had to be asked for one at a time.
Turn on, turn off, open and close now offer the areas holding something they can act on, alongside their individual entities, and target the area itself. Home Assistant scopes an area target by the calling service's own domain, so turning on a room's lights leaves its television alone.
No new shortcuts: the four commands that already exist gained the rows, which also keeps the app within Apple's cap of ten.
Targets are named for what they hold, down to the cover device class where a room agrees on one, so a room of curtains reads as "Office curtains" rather than "Office covers". Matching takes the area's Home Assistant aliases as well, which is what people set them for.
Based on #5683, which the open and close changes here build on.
Screenshots
The rows appear in Siri's and the Shortcuts app's own pickers, which the app doesn't draw.
Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#
Any other notes
An area target's id is namespaced (
serverId-area:areaId:domain) so it can never collide with an entity's, and keyed on the domain rather than the device class so a room whose curtains become blinds keeps its saved shortcuts working.An area with a single light still gets a row: "turn on the kitchen lights" is how people speak whether the kitchen holds one lamp or five.
Toggling an area calls the domain's own toggle service, since a room has no single state to read. Area commands get the spoken sentence and no result card, for the same reason.