iOS/Sources/Shared/Intents/GetCameraImageIntentHandler.swift
Bruno Pantaleão Gonçalves 5ae4dfb563
Improve cameras list view (#4175)
<!-- 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 -->

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->
<img width="2756" height="1368" alt="CleanShot 2026-01-07 at 14 27
46@2x"
src="https://github.com/user-attachments/assets/092f6241-06b1-47b5-802f-468d22fa4473"
/>

## 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. -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-07 13:58:09 +00:00

114 lines
4.3 KiB
Swift

import Foundation
import Intents
import MobileCoreServices
import PromiseKit
import UIKit
class GetCameraImageIntentHandler: NSObject, GetCameraImageIntentHandling {
typealias Intent = GetCameraImageIntent
func resolveServer(for intent: Intent, with completion: @escaping (IntentServerResolutionResult) -> Void) {
if let server = Current.servers.server(for: intent) {
completion(.success(with: .init(server: server)))
} else {
completion(.needsValue())
}
}
func provideServerOptions(for intent: Intent, with completion: @escaping ([IntentServer]?, Error?) -> Void) {
completion(IntentServer.all, nil)
}
func provideServerOptionsCollection(
for intent: Intent,
with completion: @escaping (INObjectCollection<IntentServer>?, Error?) -> Void
) {
completion(.init(items: IntentServer.all), nil)
}
func resolveCameraID(
for intent: Intent,
with completion: @escaping (INStringResolutionResult) -> Void
) {
if let cameraID = intent.cameraID, cameraID.hasPrefix("camera.") {
Current.Log.info("using given \(cameraID)")
completion(.success(with: cameraID))
} else {
Current.Log.info("loading values due to no camera id")
completion(.needsValue())
}
}
func provideCameraIDOptions(
for intent: Intent,
with completion: @escaping ([String]?, Error?) -> Void
) {
guard let server = Current.servers.server(for: intent),
let connection = Current.api(for: server)?.connection else {
completion(nil, PickAServerError.error)
return
}
connection.caches.states().once().promise.map(\.all)
.filterValues { $0.domain == "camera" }
.mapValues(\.entityId)
.sortedValues()
.done { completion($0, nil) }
.catch { completion(nil, $0) }
}
func provideCameraIDOptionsCollection(
for intent: Intent,
with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void
) {
provideCameraIDOptions(for: intent) { identifiers, error in
completion(identifiers.flatMap { .init(items: $0.map { $0 as NSString }) }, error)
}
}
func handle(intent: Intent, completion: @escaping (GetCameraImageIntentResponse) -> Void) {
guard let server = Current.servers.server(for: intent), let api = Current.api(for: server) else {
completion(
.failure(
error: "No server provided, active URL: \(String(describing: Current.servers.server(for: intent)?.info.connection.activeURL()?.absoluteString))"
)
)
return
}
if let cameraID = intent.cameraID {
Current.Log.verbose("Getting camera frame for \(cameraID)")
api.getCameraSnapshot(cameraEntityID: cameraID).done { frame in
Current.Log.verbose("Successfully got camera image during shortcut")
guard let pngData = frame.pngData() else {
Current.Log.error("Image data could not be converted to PNG")
completion(.failure(error: "Image could not be converted to PNG"))
return
}
let resp = GetCameraImageIntentResponse(code: .success, userActivity: nil)
resp.cameraImage = INFile(
data: pngData,
filename: "\(cameraID)_still.png",
typeIdentifier: kUTTypePNG as String
)
resp.cameraID = cameraID
completion(resp)
}.catch { error in
Current.Log.error("Error when getting camera image in shortcut \(error)")
let resp = GetCameraImageIntentResponse(code: .failure, userActivity: nil)
resp.error = "Error during api.GetCameraImage: \(error.localizedDescription)"
completion(resp)
}
} else {
Current.Log.error("Unable to unwrap intent.cameraID")
let resp = GetCameraImageIntentResponse(code: .failure, userActivity: nil)
resp.error = "Unable to unwrap intent.cameraID"
completion(resp)
}
}
}