Files
iOS/Sources/Extensions/AppIntents/PerformActionAppIntent.swift
Bruno Pantaleão Gonçalves 00d3852f22 Improve "Perform action" App Intent (#4720)
<!-- 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 adds the capability of receive a response from the perform
action App Intent + some small improvements on actions list rendering

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->
<img width="1792" height="1550" alt="CleanShot 2026-06-09 at 21 06
09@2x"
src="https://github.com/user-attachments/assets/3036b9cd-0992-46d3-840f-275725a543ac"
/>

## 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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 09:13:32 +02:00

100 lines
3.3 KiB
Swift

import AppIntents
import Foundation
import Shared
@available(iOS 17.0, *)
struct PerformActionAppIntent: AppIntent {
static var title: LocalizedStringResource = .init(
"app_intents.perform_action.title",
defaultValue: "Perform action"
)
static var description = IntentDescription(.init(
"app_intents.perform_action.description",
defaultValue: "Perform an action on a Home Assistant server"
))
static var parameterSummary: some ParameterSummary {
Summary {
\.$server
\.$action
\.$payload
}
}
@Parameter(title: .init("app_intents.server.title", defaultValue: "Server"))
var server: IntentServerAppEntity
@Parameter(
title: .init("app_intents.perform_action.action.title", defaultValue: "Action"),
requestValueDialog: IntentDialog(.init(
"app_intents.perform_action.action_parameter_configuration",
defaultValue: "Which action?"
))
)
var action: IntentActionEntity
@Parameter(
title: .init("app_intents.perform_action.payload.title", defaultValue: "Action data"),
description: .init(
"app_intents.perform_action.payload.description",
defaultValue: "JSON data to send with the action"
),
default: "{}",
inputOptions: .init(
capitalizationType: .none,
multiline: true,
autocorrect: false,
smartQuotes: false,
smartDashes: false
)
)
var payload: String
func perform() async throws -> some IntentResult & ReturnsValue<String> {
await Current.connectivity.syncNetworkInformation()
guard action.serverId == server.id,
let server = server.getServer(),
let api = Current.api(for: server) else {
throw ShortcutAppIntentError(L10n.AppIntents.Error.noServer)
}
let payloadDict = try Self.payloadDictionary(from: payload)
let components = action.actionId.split(separator: ".")
guard components.count == 2 else {
throw ShortcutAppIntentError(L10n.AppIntents.PerformAction.Error.invalidAction)
}
do {
let response = try await api.callServiceWithResponse(
domain: String(components[0]),
service: String(components[1]),
serviceData: payloadDict,
returnResponse: action.supportsResponse
).async()
if let json = response.jsonString() {
return .result(value: json)
}
} catch {
throw ShortcutAppIntentError(L10n.AppIntents.PerformAction.responseFailure(error.localizedDescription))
}
return .result(value: L10n.AppIntents.PerformAction.responseSuccess)
}
private static func payloadDictionary(from payload: String) throws -> [String: Any] {
guard payload.isEmpty == false else { return [:] }
let data = Data(payload.utf8)
guard let jsonObject = try JSONSerialization.jsonObject(
with: data,
options: .allowFragments
) as? [String: Any] else {
throw ShortcutAppIntentError(L10n.AppIntents.PerformAction.Error.invalidPayload)
}
return jsonObject
}
}