Files
iOS/Sources/Shared/API/CallServiceResponse.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

49 lines
1.8 KiB
Swift

import Foundation
import HAKit
/// Response of a websocket `call_service` performed with `return_response: true`.
///
/// Home Assistant returns `{ "context": { ... }, "response": <any> }` for actions that support a
/// response (`SupportsResponse.OPTIONAL` / `.ONLY`). The shape of `response` depends on the action,
/// so the raw value is kept untyped and can be JSON-serialized by callers.
public struct CallServiceResponse: HADataDecodable {
/// The raw `response` value returned by the action, if any.
public let response: Any?
public init(data: HAData) throws {
if case let .dictionary(dictionary) = data {
self.response = dictionary["response"]
} else {
self.response = nil
}
}
/// Whether the action returned a non-empty response value.
public var hasResponse: Bool {
switch response {
case nil, is NSNull:
return false
case let dictionary as [String: Any]:
return dictionary.isEmpty == false
case let array as [Any]:
return array.isEmpty == false
default:
return true
}
}
/// The `response` value serialized as a JSON string, or nil when there is nothing to serialize.
public func jsonString() -> String? {
guard hasResponse, let response else { return nil }
// Wrap primitives so JSONSerialization (which requires a top-level container) can encode them.
let serializable: Any = (response is [String: Any] || response is [Any]) ? response : ["response": response]
guard JSONSerialization.isValidJSONObject(serializable),
let data = try? JSONSerialization.data(withJSONObject: serializable, options: [.sortedKeys]),
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
}