Files
iOS/Sources/Extensions/Widgets/HAAppEntityAppIntentEntity.swift
Bruno Pantaleão Gonçalves ce1fe1b485 Extract helper for entity intent collections (#4416)
<!-- Thank you for submitting a Pull Request and helping to improve Home
Assistant. Please complete the following sections to help the processing
and review of your changes. Please do not delete anything from this
template. -->

## Summary
<!-- Provide a brief summary of the changes you have made and most
importantly what they aim to achieve -->
Introduce makeHAEntityIntentItemCollection to centralize building
IntentItemCollection<HAAppEntityAppIntentEntity> with a default icon and
areaName mapping. Replace duplicated mapping logic in multiple
ControlOpen* entity option providers (Camera, Cover, InputBoolean,
Light, Lock, Sensor, Switch) to call the new helper, reducing
duplication and adding consistent areaName support. The helper is added
to HAAppEntityAppIntentEntity.swift and used by the various
ControlOpen*ValueProvider files.

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->

## Link to pull request in Documentation repository
<!-- Pull requests that add, change or remove functionality must have a
corresponding pull request in the Companion App Documentation repository
(https://github.com/home-assistant/companion.home-assistant). Please add
the number of this pull request after the "#" -->
Documentation: home-assistant/companion.home-assistant#

## Any other notes
<!-- If there is any other information of note, like if this Pull
Request is part of a bigger change, please include it here. -->
2026-03-05 14:52:44 +00:00

138 lines
4.6 KiB
Swift

import AppIntents
import Foundation
import SFSafeSymbols
import Shared
import WidgetKit
@available(iOS 16.4, macOS 13.0, watchOS 9.0, *)
struct HAAppEntityAppIntentEntity: AppEntity {
static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Entity")
static let defaultQuery = HAAppEntityAppIntentEntityQuery()
var id: String
var entityId: String
var serverId: String
var serverName: String
var areaName: String?
var deviceName: String?
var displayString: String
var iconName: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(displayString)",
subtitle: .init(stringLiteral: subtitle)
)
}
private var subtitle: String {
var subtitle = ""
if let areaName {
subtitle += areaName
}
if let deviceName,
deviceName.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() != displayString.lowercased()
.trimmingCharacters(in: .whitespacesAndNewlines) {
if subtitle.isEmpty {
subtitle += deviceName
} else {
subtitle += "\(deviceName)"
}
}
return subtitle
}
init(
id: String,
entityId: String,
serverId: String,
serverName: String,
areaName: String? = nil,
deviceName: String? = nil,
displayString: String,
iconName: String
) {
self.id = id
self.entityId = entityId
self.serverId = serverId
self.serverName = serverName
self.areaName = areaName
self.deviceName = deviceName
self.displayString = displayString
self.iconName = iconName
}
}
@available(iOS 16.4, macOS 13.0, watchOS 9.0, *)
struct HAAppEntityAppIntentEntityQuery: EntityQuery, EntityStringQuery {
func entities(for identifiers: [String]) async throws -> [HAAppEntityAppIntentEntity] {
getEntities().flatMap(\.1).filter { identifiers.contains($0.id) }
}
func entities(matching string: String) async throws -> IntentItemCollection<HAAppEntityAppIntentEntity> {
.init(sections: getEntities(matching: string).map { (key: Server, value: [HAAppEntityAppIntentEntity]) in
.init(
.init(stringLiteral: key.info.name),
items: value
)
})
}
func suggestedEntities() async throws -> IntentItemCollection<HAAppEntityAppIntentEntity> {
.init(sections: getEntities().map { (key: Server, value: [HAAppEntityAppIntentEntity]) in
.init(.init(stringLiteral: key.info.name), items: value)
})
}
private func getEntities(matching string: String? = nil) -> [(Server, [HAAppEntityAppIntentEntity])] {
var allEntities: [(Server, [HAAppEntityAppIntentEntity])] = []
let entities = ControlEntityProvider(domains: []).getEntities(matching: string)
for (server, values) in entities {
let deviceMap = values.devicesMap(for: server.identifier.rawValue)
let areasMap = values.areasMap(for: server.identifier.rawValue)
allEntities.append((server, values.map({ entity in
HAAppEntityAppIntentEntity(
id: entity.id,
entityId: entity.entityId,
serverId: entity.serverId,
serverName: server.info.name,
areaName: areasMap[entity.entityId]?.name ?? "",
deviceName: deviceMap[entity.entityId]?.name ?? "",
displayString: entity.name,
iconName: entity.icon ?? SFSymbol.applescriptFill.rawValue
)
})))
}
return allEntities
}
}
@available(iOS 16.4, macOS 13.0, watchOS 9.0, *)
func makeHAEntityIntentItemCollection(
entities: [(Server, [HAAppEntity])],
defaultIconName: String
) -> IntentItemCollection<HAAppEntityAppIntentEntity> {
.init(sections: entities.map { (server: Server, values: [HAAppEntity]) in
let areasMap = values.areasMap(for: server.identifier.rawValue)
return .init(
.init(stringLiteral: server.info.name),
items: values.map { entity in
HAAppEntityAppIntentEntity(
id: entity.id,
entityId: entity.entityId,
serverId: entity.serverId,
serverName: server.info.name,
areaName: areasMap[entity.entityId]?.name,
displayString: entity.name,
iconName: entity.icon ?? defaultIconName
)
}
)
})
}