Skip to content

Commit

Permalink
Improve iOS Actions, facilitate automation creation (#2541)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgoncal authored Jan 30, 2024
1 parent e256e96 commit 8ad58ae
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 58 deletions.
5 changes: 5 additions & 0 deletions Sources/App/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
"actions_configurator.title" = "New Action";
"actions_configurator.trigger_example.share" = "Share Contents";
"actions_configurator.trigger_example.title" = "Example Trigger";
"actions_configurator.action.title" = "Execute";
"actions_configurator.action.create_automation" = "Create automation";
"actions_configurator.action.footer" = "Define what will be executed when Action is performed, alternatively you can use the example trigger below manually.";
"actions_configurator.visual_section.scene_defined" = "The appearance of this action is controlled by the scene configuration.";
"actions_configurator.visual_section.scene_hint_footer" = "You can also change these by customizing the Scene attributes: %@";
"actions_configurator.visual_section.server_defined" = "The appearance of this action is controlled by the server configuration.";
Expand All @@ -42,6 +45,8 @@
"alerts.open_url_from_notification.title" = "Open URL?";
"alerts.prompt.cancel" = "Cancel";
"alerts.prompt.ok" = "OK";
"alerts.action_automation_editor.unavailable.title" = "Please update Home Assistant";
"alerts.action_automation_editor.unavailable.body" = "To automatically create an automation for an Action please update your Home Assistant to at least version 2024.2";
"always_open_label" = "Always Open";
"announcement.drop_support.button" = "Continue";
"announcement.drop_support.button_read_more" = "Read more";
Expand Down
90 changes: 34 additions & 56 deletions Sources/App/Settings/ActionConfigurator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
}
}

var newAction: Bool = true
var shouldSave: Bool = false
var preview = ActionPreview(frame: CGRect(x: 0, y: 0, width: 169, height: 44))
private var newAction: Bool = true
private(set) var shouldSave: Bool = false
private(set) var shouldOpenAutomationEditor: Bool = false
private var preview = ActionPreview(frame: CGRect(x: 0, y: 0, width: 169, height: 55))

convenience init(action: Action?) {
self.init()
Expand Down Expand Up @@ -52,6 +53,18 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
}
}

form +++ ViewRow<ActionPreview>("preview") { [weak self] row in
row.hidden = Condition.function(["showInWatch"], { _ in
!(self?.action.showInWatch ?? true)
})
}.cellSetup { [weak self] cell, _ in
guard let self else { return }
cell.backgroundColor = UIColor.clear
cell.preservesSuperviewLayoutMargins = false
self.updatePreviews()
cell.view = self.preview
}

let firstSection = Section()
form +++ firstSection

Expand All @@ -71,8 +84,7 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
}
}

let visuals = Section(
)
let visuals = Section()

if action.canConfigure(\Action.Text) || action.isServerControlled {
let section: Section
Expand Down Expand Up @@ -135,12 +147,6 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
}
}

// After text if uneditable
firstSection <<< VoiceShortcutRow {
$0.buttonStyle = .automaticOutline
$0.value = .intent(PerformActionIntent(action: action))
}

if action.canConfigure(\Action.TextColor) {
visuals <<< InlineColorPickerRow("text_color") {
$0.title = L10n.ActionsConfigurator.Rows.TextColor.title
Expand Down Expand Up @@ -230,15 +236,6 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
}
}
} else {
// only show cancel/save flow for editable actions
navigationItem.leftBarButtonItems = [
UIBarButtonItem(
barButtonSystemItem: .cancel,
target: self,
action: #selector(cancel)
),
]

navigationItem.rightBarButtonItems = [
UIBarButtonItem(
barButtonSystemItem: .save,
Expand All @@ -261,15 +258,16 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
form.append(visuals)
}

form +++ ViewRow<ActionPreview>("preview") { [weak self] row in
row.hidden = Condition.function(["showInWatch"], { _ in
!(self?.action.showInWatch ?? true)
form +++ Section(header: L10n.ActionsConfigurator.Action.title, footer: L10n.ActionsConfigurator.Action.footer)
<<< ButtonRow {
$0.title = L10n.ActionsConfigurator.Action.createAutomation
}.onCellSelection({ [weak self] _, _ in
self?.saveAndAutomate()
})
}.cellSetup { [weak self] cell, _ in
cell.backgroundColor = UIColor.clear
cell.preservesSuperviewLayoutMargins = false
self?.updatePreviews()
cell.view = self?.preview

form +++ VoiceShortcutRow {
$0.buttonStyle = .automaticOutline
$0.value = .intent(PerformActionIntent(action: action))
}

form +++ YamlSection(
Expand All @@ -288,11 +286,6 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {
)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@objc
func getInfoAction(_ sender: Any) {
Current.Log.verbose("getInfoAction hit, open docs page!")
Expand All @@ -304,20 +297,18 @@ class ActionConfigurator: HAFormViewController, TypedRowControllerType {

if form.validate().count == 0 {
Current.Log.verbose("Category form is valid, calling dismiss callback!")

shouldSave = true

onDismissCallback?(self)
}
}

@objc
func cancel(_ sender: Any) {
Current.Log.verbose("Cancel hit, calling dismiss")

shouldSave = false

onDismissCallback?(self)
private func saveAndAutomate() {
if form.validate().count == 0 {
Current.Log.verbose("Category form is valid, calling dismiss callback!")
shouldSave = true
shouldOpenAutomationEditor = true
onDismissCallback?(self)
}
}

private func updatePreviews() {
Expand All @@ -343,20 +334,7 @@ class ActionPreview: UIView {
override func layoutSubviews() {
super.layoutSubviews()

layer.cornerRadius = 5.0

layer.cornerRadius = 2.0
layer.borderWidth = 1.0
layer.borderColor = UIColor.clear.cgColor
layer.masksToBounds = true

layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius).cgPath

layer.cornerRadius = 8
let centerY = (frame.size.height / 2) - 50

title = UILabel(frame: CGRect(x: 60, y: centerY, width: 200, height: 100))
Expand Down
7 changes: 6 additions & 1 deletion Sources/App/Settings/Eureka/YamlSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public final class YamlSection: Section {
tag: String,
header: String,
yamlGetter: @escaping () -> String,
isEditable: Bool = false,
present: @escaping (UIViewController) -> Void
) {
self.yamlGetter = yamlGetter
Expand All @@ -31,7 +32,11 @@ public final class YamlSection: Section {
self.tag = tag

self
<<< yamlRow
<<< yamlRow.cellSetup({ cell, _ in
cell.textView.isEditable = false
}).cellUpdate({ cell, _ in
cell.textView.isEditable = false
})
<<< ButtonRow { row in
row.title = L10n.ActionsConfigurator.TriggerExample.share

Expand Down
9 changes: 9 additions & 0 deletions Sources/App/Settings/SettingsDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,15 @@ class SettingsDetailViewController: HAFormViewController, TypedRowControllerType
}.done {
self?.updatePositions()
}.cauterize()

if vc.shouldOpenAutomationEditor {
vc.navigationController?.dismiss(animated: true, completion: {
Current.sceneManager.webViewWindowControllerPromise.then(\.webViewControllerPromise)
.done { controller in
controller.openActionAutomationEditor(actionId: vc.action.ID)
}
})
}
}
})
}
Expand Down
30 changes: 30 additions & 0 deletions Sources/App/WebView/WebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,36 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
}
)
}

public func openActionAutomationEditor(actionId: String) {
guard server.info.version >= .externalBusCommandAutomationEditor else {
showActionAutomationEditorNotAvailable()
return
}
sendExternalBus(message: .init(command: "automation/editor/show", payload: [
"config": [
"trigger": [
[
"platform": "event",
"event_type": "ios.action_fired",
"event_data": [
"actionID": actionId,
],
],
],
],
]))
}

private func showActionAutomationEditorNotAvailable() {
let alert = UIAlertController(
title: L10n.Alerts.ActionAutomationEditor.Unavailable.title,
message: L10n.Alerts.ActionAutomationEditor.Unavailable.body,
preferredStyle: .alert
)
alert.addAction(.init(title: L10n.okLabel, style: .default))
present(alert, animated: true)
}
}

extension String {
Expand Down
6 changes: 5 additions & 1 deletion Sources/Shared/API/WebSocket/WebSocketMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ public class WebSocketMessage: Codable {
self.command = nil
}

public init(command: String) {
public init(command: String, payload: [String: Any]? = nil) {
self.ID = -1
self.MessageType = "command"
self.command = command
self.Payload = payload
}

public func encode(to encoder: Encoder) throws {
Expand All @@ -82,6 +83,9 @@ public class WebSocketMessage: Codable {
if let Result = Result {
try container.encode(Result, forKey: .Result)
}
if let Payload {
try container.encode(Payload, forKey: .Payload)
}
try container.encodeIfPresent(command, forKey: .command)
}

Expand Down
1 change: 1 addition & 0 deletions Sources/Shared/Environment/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public extension Version {
static var fullWebhookSecretKey: Version = .init(major: 2022, minor: 3)
static var conversationWebhook: Version = .init(major: 2023, minor: 2, prerelease: "any0")
static var externalBusCommandSidebar: Version = .init(major: 2023, minor: 4, prerelease: "b3")
static var externalBusCommandAutomationEditor: Version = .init(major: 2024, minor: 2, prerelease: "any0")

var coreRequiredString: String {
L10n.requiresVersion(String(format: "core-%d.%d", major, minor ?? -1))
Expand Down
16 changes: 16 additions & 0 deletions Sources/Shared/Resources/Swiftgen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ public enum L10n {
public enum ActionsConfigurator {
/// New Action
public static var title: String { return L10n.tr("Localizable", "actions_configurator.title") }
public enum Action {
/// Create automation
public static var createAutomation: String { return L10n.tr("Localizable", "actions_configurator.action.create_automation") }
/// Define what will be executed when Action is performed, alternatively you can use the example trigger below manually.
public static var footer: String { return L10n.tr("Localizable", "actions_configurator.action.footer") }
/// Execute
public static var title: String { return L10n.tr("Localizable", "actions_configurator.action.title") }
}
public enum Rows {
public enum BackgroundColor {
/// Background Color
Expand Down Expand Up @@ -170,6 +178,14 @@ public enum L10n {
}

public enum Alerts {
public enum ActionAutomationEditor {
public enum Unavailable {
/// To automatically create an automation for an Action please update your Home Assistant to at least version 2024.2
public static var body: String { return L10n.tr("Localizable", "alerts.action_automation_editor.unavailable.body") }
/// Please update Home Assistant
public static var title: String { return L10n.tr("Localizable", "alerts.action_automation_editor.unavailable.title") }
}
}
public enum Alert {
/// OK
public static var ok: String { return L10n.tr("Localizable", "alerts.alert.ok") }
Expand Down

0 comments on commit 8ad58ae

Please sign in to comment.