mirror of
https://github.com/home-assistant/iOS.git
synced 2026-06-16 04:16:39 -05:00
<!-- 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 --> This PR deprecate intents from siri intents definition and migrates them to App Intent. ## 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. -->
122 lines
4.2 KiB
Swift
122 lines
4.2 KiB
Swift
import AppIntents
|
|
import HAKit
|
|
import PromiseKit
|
|
import Shared
|
|
|
|
@available(iOS 17.0, *)
|
|
struct IntentCameraEntity: AppEntity {
|
|
static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Camera")
|
|
static let defaultQuery = IntentCameraEntityQuery()
|
|
|
|
let id: String
|
|
let serverId: String
|
|
let entityId: String
|
|
let displayName: String
|
|
|
|
var displayRepresentation: DisplayRepresentation {
|
|
.init(
|
|
title: .init(stringLiteral: displayName),
|
|
subtitle: .init(stringLiteral: entityId),
|
|
image: .init(systemName: "camera")
|
|
)
|
|
}
|
|
}
|
|
|
|
@available(iOS 17.0, *)
|
|
struct IntentCameraEntityQuery: EntityQuery, EntityStringQuery {
|
|
@IntentParameterDependency<GetCameraSnapshotAppIntent>(\.$server)
|
|
var intent
|
|
|
|
func entities(for identifiers: [String]) async throws -> [IntentCameraEntity] {
|
|
let cameras = try await cameraEntities().flatMap(\.1)
|
|
let matchedCameras = cameras.filter { identifiers.contains($0.id) }
|
|
let matchedIdentifiers = Set(matchedCameras.map(\.id))
|
|
let fallbackCameras = identifiers
|
|
.filter { matchedIdentifiers.contains($0) == false }
|
|
.compactMap(Self.cameraEntity(for:))
|
|
return matchedCameras + fallbackCameras
|
|
}
|
|
|
|
func entities(matching string: String) async throws -> IntentItemCollection<IntentCameraEntity> {
|
|
try await cameraCollection(matching: string)
|
|
}
|
|
|
|
func suggestedEntities() async throws -> IntentItemCollection<IntentCameraEntity> {
|
|
try await cameraCollection()
|
|
}
|
|
|
|
private func cameraCollection(matching string: String? = nil) async throws
|
|
-> IntentItemCollection<IntentCameraEntity> {
|
|
let sections = try await cameraEntities().map { server, cameras in
|
|
let filteredCameras: [IntentCameraEntity]
|
|
if let string, string.isEmpty == false {
|
|
filteredCameras = cameras.filter {
|
|
$0.displayName.localizedCaseInsensitiveContains(string)
|
|
|| $0.entityId.localizedCaseInsensitiveContains(string)
|
|
}
|
|
} else {
|
|
filteredCameras = cameras
|
|
}
|
|
return IntentItemSection<IntentCameraEntity>(
|
|
.init(stringLiteral: server.info.name),
|
|
items: filteredCameras
|
|
)
|
|
}
|
|
return .init(sections: sections)
|
|
}
|
|
|
|
private func cameraEntities() async throws -> [(Server, [IntentCameraEntity])] {
|
|
guard let server = intent?.server.getServer(),
|
|
let connection = Current.api(for: server)?.connection else {
|
|
return []
|
|
}
|
|
|
|
let cameras = try await connection.cameraEntities().async(timeout: 10)
|
|
return [(
|
|
server,
|
|
cameras.map { entity in
|
|
Self.cameraEntity(server: server, entity: entity)
|
|
}
|
|
)]
|
|
}
|
|
|
|
private static func cameraEntity(server: Server, entity: HAEntity) -> IntentCameraEntity {
|
|
IntentCameraEntity(
|
|
id: "\(server.identifier.rawValue)::\(entity.entityId)",
|
|
serverId: server.identifier.rawValue,
|
|
entityId: entity.entityId,
|
|
displayName: entity.attributes.friendlyName ?? entity.entityId
|
|
)
|
|
}
|
|
|
|
private static func cameraEntity(for identifier: String) -> IntentCameraEntity? {
|
|
let components = identifier.components(separatedBy: "::")
|
|
guard components.count == 2 else {
|
|
return nil
|
|
}
|
|
|
|
return IntentCameraEntity(
|
|
id: identifier,
|
|
serverId: components[0],
|
|
entityId: components[1],
|
|
displayName: components[1]
|
|
)
|
|
}
|
|
}
|
|
|
|
private extension HAConnection {
|
|
func cameraEntities() -> Promise<[HAEntity]> {
|
|
caches.states().once().promise
|
|
.map(\.all)
|
|
.filterValues { $0.domain == "camera" }
|
|
.mapValues { $0 }
|
|
.map { entities in
|
|
entities.sorted {
|
|
($0.attributes.friendlyName ?? $0.entityId).localizedCaseInsensitiveCompare(
|
|
$1.attributes.friendlyName ?? $1.entityId
|
|
) == .orderedAscending
|
|
}
|
|
}
|
|
}
|
|
}
|