Skip to content

Commit 5f1ae83

Browse files
authored
Make app database update in settings on-demand trigger (#4244)
<!-- 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. --> ## 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. -->
1 parent ac1687b commit 5f1ae83

File tree

9 files changed

+44
-18
lines changed

9 files changed

+44
-18
lines changed

Sources/App/Resources/en.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,3 +1454,4 @@ Home Assistant is open source, advocates for privacy and runs locally in your ho
14541454
"database_updater.toast.title" = "Updating %@";
14551455
"database_updater.toast.syncing" = "Syncing server data...";
14561456
"database_updater.toast.syncing_with_progress" = "Syncing server data... (%d/%d)";
1457+
"settings.connection_section.refresh_server" = "Update server information";

Sources/App/Settings/Connection/ConnectionSettingsView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ struct ConnectionSettingsView: View {
275275
.contentShape(Rectangle())
276276
}
277277
.buttonStyle(.plain)
278+
279+
Button {
280+
viewModel.updateAppDatabase()
281+
} label: {
282+
Label(L10n.Settings.ConnectionSection.refreshServer, systemSymbol: .arrowClockwise)
283+
}
278284
}
279285
}
280286

Sources/App/Settings/Connection/ConnectionSettingsViewModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ final class ConnectionSettingsViewModel: ObservableObject {
6363
}
6464
}
6565

66+
func updateAppDatabase() {
67+
server.refreshAppDatabase(forceUpdate: true)
68+
}
69+
6670
// MARK: - Setup
6771

6872
private func setupObservers() {

Sources/App/Settings/Settings/ServersListView.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ struct ServersListView: View {
1111
NavigationLink(destination: ConnectionSettingsView(server: server)) {
1212
HomeAssistantAccountRowView(server: server)
1313
}
14+
.contextMenu {
15+
Button {
16+
server.refreshAppDatabase(forceUpdate: true)
17+
} label: {
18+
Label(L10n.Settings.ConnectionSection.refreshServer, systemSymbol: .arrowClockwise)
19+
}
20+
}
1421
}
1522
.onMove { source, destination in
1623
observer.moveServers(from: source, to: destination)

Sources/App/Settings/Settings/SettingsView.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ struct SettingsView: View {
99
@EnvironmentObject private var viewControllerProvider: ViewControllerProvider
1010
@StateObject private var serversObserver = ServersObserver()
1111

12-
@State private var appDatabaseUpdaterTask: Task<Void, Never>?
13-
1412
var body: some View {
1513
Group {
1614
if Current.isCatalyst {
@@ -19,14 +17,6 @@ struct SettingsView: View {
1917
iOSView
2018
}
2119
}
22-
.onAppear {
23-
appDatabaseUpdaterTask?.cancel()
24-
appDatabaseUpdaterTask = Task {
25-
for server in Current.servers.all {
26-
await Current.appDatabaseUpdater.update(server: server)
27-
}
28-
}
29-
}
3020
}
3121

3222
// MARK: - macOS Split View

Sources/App/WebView/WebViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
759759
/// Called after view appears and on pull to refresh to avoid blocking app launch
760760
private func updateDatabaseAndPanels() {
761761
// Update runs in background automatically, returns immediately
762-
Current.appDatabaseUpdater.update(server: server)
762+
Current.appDatabaseUpdater.update(server: server, forceUpdate: false)
763763
Current.panelsUpdater.update()
764764
}
765765

Sources/Shared/API/Server.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,13 @@ public final class Server: Hashable, Comparable, CustomStringConvertible {
247247
identifier.description
248248
}
249249
}
250+
251+
#if !os(watchOS)
252+
public extension Server {
253+
/// Triggers a refresh of this server's data from Home Assistant
254+
/// - Parameter forceUpdate: Whether to force the update regardless of cache state. Defaults to `false`.
255+
func refreshAppDatabase(forceUpdate: Bool = false) {
256+
Current.appDatabaseUpdater.update(server: self, forceUpdate: forceUpdate)
257+
}
258+
}
259+
#endif

Sources/Shared/Environment/AppDatabaseUpdater.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import UIKit
1010
/// applies per-server throttling with backoff, and performs careful cancellation and batched DB writes.
1111
public protocol AppDatabaseUpdaterProtocol {
1212
func stop()
13-
func update(server: Server)
13+
func update(server: Server, forceUpdate: Bool)
1414
}
1515

1616
final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
@@ -155,9 +155,10 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
155155
/// Starts an update for a specific server in the background.
156156
/// This method returns immediately and does not block the caller.
157157
/// - Parameter server: The specific server to update.
158+
/// - Parameter forceUpdate: Forces update regardless of other conditions
158159
/// - Server updates are queued and processed sequentially, one at a time.
159160
/// - Applies per-server throttling with exponential backoff on failures.
160-
func update(server: Server) {
161+
func update(server: Server, forceUpdate: Bool) {
161162
// Explicitly detach from the calling context to ensure we don't block the main thread
162163
// Returns immediately while work continues in the background
163164
Task.detached(priority: .userInitiated) { [weak self] in
@@ -169,7 +170,7 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
169170
await taskCoordinator.enqueueUpdate(serverId: serverId) { [weak self] in
170171
guard let self else { return }
171172

172-
Current.Log.verbose("Updating database for server \(server.info.name)")
173+
Current.Log.verbose("Updating database for server \(server.info.name)\(forceUpdate ? " (forced)" : "")")
173174

174175
// Show toast indicating update has started
175176
await showUpdateToast(for: server)
@@ -185,7 +186,7 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
185186
}
186187
}
187188

188-
await performSingleServerUpdate(server: server)
189+
await performSingleServerUpdate(server: server, forceUpdate: forceUpdate)
189190
}
190191

191192
// Store the task for this server
@@ -197,10 +198,15 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
197198
}
198199

199200
/// Determines if a specific server should be updated based on connection and throttle rules.
200-
private func shouldUpdateServer(_ server: Server) -> Bool {
201+
private func shouldUpdateServer(_ server: Server, forceUpdate: Bool) -> Bool {
201202
guard server.info.connection.activeURL() != nil else { return false }
202203
if isUpdateCancelled() { return false }
203204

205+
// Skip throttle checks if forceUpdate is true
206+
if forceUpdate {
207+
return true
208+
}
209+
204210
// Per-server throttle with exponential backoff
205211
if let last = perServerLastUpdate[server.identifier.rawValue] {
206212
let failures = consecutiveFailuresByServer[server.identifier.rawValue] ?? 0
@@ -212,9 +218,9 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
212218
}
213219

214220
/// Performs an update for a single specific server.
215-
private func performSingleServerUpdate(server: Server) async {
221+
private func performSingleServerUpdate(server: Server, forceUpdate: Bool) async {
216222
guard !isUpdateCancelled() else { return }
217-
guard shouldUpdateServer(server) else {
223+
guard shouldUpdateServer(server, forceUpdate: forceUpdate) else {
218224
Current.Log.verbose("Skipping update for server \(server.info.name) - throttled")
219225
return
220226
}

Sources/Shared/Resources/Swiftgen/Strings.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,8 @@ public enum L10n {
25402540
public static var localPushDescription: String { return L10n.tr("Localizable", "settings.connection_section.local_push_description") }
25412541
/// Logged in as
25422542
public static var loggedInAs: String { return L10n.tr("Localizable", "settings.connection_section.logged_in_as") }
2543+
/// Update server information
2544+
public static var refreshServer: String { return L10n.tr("Localizable", "settings.connection_section.refresh_server") }
25432545
/// Servers
25442546
public static var servers: String { return L10n.tr("Localizable", "settings.connection_section.servers") }
25452547
/// Reorder to define default server

0 commit comments

Comments
 (0)