diff --git a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift index cd950b5b8..545aec7c2 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/OpenHABItemCache.swift @@ -32,11 +32,23 @@ public actor OpenHABItemCache { } public func getCachedItems(home: UUID) async -> [OpenHABItem]? { + // Validate home exists in storedHomes before trying to load + let storedHomes = await Preferences.shared.storedHomes + guard storedHomes[home] != nil else { + Logger.itemCache.error("Cannot get cached items: home \(home) not found in storedHomes. Available homes: \(storedHomes.keys)") + return nil + } await reloadCacheIfNeeded(homes: [home]) return items[home] } public func getCachedItem(name: String, home: UUID) async -> [OpenHABItem]? { + // Validate home exists in storedHomes before trying to load + let storedHomes = await Preferences.shared.storedHomes + guard storedHomes[home] != nil else { + Logger.itemCache.error("Cannot get cached item: home \(home) not found in storedHomes. Available homes: \(storedHomes.keys)") + return nil + } await reloadCacheIfNeeded(homes: [home]) return items[home]?.filter { $0.name == name } } @@ -86,11 +98,17 @@ public actor OpenHABItemCache { do { let loadedItems = try await loadNonGroupItemsForHomes(homes) Logger.itemCache.info("Store loaded items in cache") - homes.forEach { items[$0] = loadedItems[$0] } + // Update cache with loaded items, ensuring we don't write nil + for homeId in homes { + items[homeId] = loadedItems[homeId] ?? [] + } let now = Date.now homes.forEach { lastLoad[$0] = now } - let itemCounts = items.map { ($0.key, $0.value.count) } - Logger.itemCache.info("Loaded \(itemCounts) items to cache") + // Log only the items that were just loaded, not the entire cache + let justLoadedCounts = loadedItems.map { ($0.key, $0.value.count) } + Logger.itemCache.info("Just loaded \(justLoadedCounts) items") + let totalCacheCount = items.map { ($0.key, $0.value.count) } + Logger.itemCache.info("Total cache now contains \(totalCacheCount) items") } catch { Logger.itemCache.error("Could not reload \(error.localizedDescription)") } @@ -153,19 +171,34 @@ public actor OpenHABItemCache { private func loadItems(homeId: UUID) async -> [OpenHABItem]? { guard let networkTracker = await assureNetworkTracker(homeId: homeId) else { - Logger.itemCache.error("Home \(homeId) not reachable") + Logger.itemCache.error("Home \(homeId) not reachable - not found in storedHomes") + return nil + } + do { + let items = try await networkTracker.getStaticItems() + Logger.itemCache.info("Successfully loaded \(items.count) items for home \(homeId)") + return items + } catch { + Logger.itemCache.error("Failed to load items for home \(homeId): \(error.localizedDescription)") return nil } - return try? await networkTracker.getStaticItems() } private func assureNetworkTracker(homeId: UUID) async -> NetworkTracker? { - if networkTrackers[homeId] == nil, let homePreferences = await Preferences.shared.storedHomes[homeId] { + if networkTrackers[homeId] == nil { + guard let homePreferences = await Preferences.shared.storedHomes[homeId] else { + Logger.itemCache.error("Home \(homeId) not found in storedHomes") + return nil + } + Logger.itemCache.info("Creating network tracker for home \(homeId)") + Logger.itemCache.info("Local: \(homePreferences.localConnectionConfig.url), Remote: \(homePreferences.remoteConnectionConfig.url)") let tracker = NetworkTracker(timeout: OpenHABItemCache.networkTimeout) networkTrackers[homeId] = tracker await tracker.startTracking(connectionConfigurations: [homePreferences.localConnectionConfig, homePreferences.remoteConnectionConfig]) + Logger.itemCache.info("Network tracker started for home \(homeId)") + } else { + Logger.itemCache.info("Using existing network tracker for home \(homeId)") } - // TODO: do we need to make sure / wait that the connection is live? return networkTrackers[homeId] } } diff --git a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift index bb51cee09..32b14418e 100644 --- a/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift +++ b/OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift @@ -263,7 +263,7 @@ public actor Preferences { @MainActor public extension Preferences { - func listStoredHomes() -> [UUID] { + func listStoredHomes() async -> [UUID] { let preferenceIds = storedHomes .sorted { e1, e2 in e1.value.homeName <= e2.value.homeName diff --git a/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index aa0e0a758..6d5944dee 100644 --- a/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/openHAB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,31 +1,12 @@ { - "originHash" : "541c72fc0c1374a607bbf8f6badadbd83932a622cb75ecf1d3c17cf1c06c9c41", "pins" : [ { "identity" : "abseil-cpp-binary", "kind" : "remoteSourceControl", "location" : "https://github.com/google/abseil-cpp-binary.git", "state" : { - "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", - "version" : "1.2024011602.0" - } - }, - { - "identity" : "alamofire", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/Alamofire.git", - "state" : { - "revision" : "f455c2975872ccd2d9c81594c658af65716e9b9a", - "version" : "5.9.1" - } - }, - { - "identity" : "alamofirenetworkactivityindicator", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/AlamofireNetworkActivityIndicator.git", - "state" : { - "revision" : "392bed083e8d193aca16bfa684ee24e4bcff0510", - "version" : "3.1.0" + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" } }, { @@ -33,17 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/app-check.git", "state" : { - "revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d", - "version" : "10.19.2" - } - }, - { - "identity" : "cocoalumberjack", - "kind" : "remoteSourceControl", - "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git", - "state" : { - "revision" : "4b8714a7fb84d42393314ce897127b3939885ec3", - "version" : "3.8.5" + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" } }, { @@ -51,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/devicekit/DeviceKit.git", "state" : { - "revision" : "d37e70cb2646666dcf276d7d3d4a9760a41ff8a6", - "version" : "4.9.0" + "revision" : "581df61650bc457ec00373a592a84be3e7468eb1", + "version" : "5.7.0" } }, { @@ -60,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/firebase-ios-sdk.git", "state" : { - "revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d", - "version" : "10.29.0" + "revision" : "087bb95235f676c1a37e928769a5b6645dcbd325", + "version" : "12.6.0" } }, { @@ -73,13 +45,22 @@ "version" : "1.4.4" } }, + { + "identity" : "google-ads-on-device-conversion-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk", + "state" : { + "revision" : "35b601a60fbbea2de3ea461f604deaaa4d8bbd0c", + "version" : "3.2.0" + } + }, { "identity" : "googleappmeasurement", "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleAppMeasurement.git", "state" : { - "revision" : "fe727587518729046fc1465625b9afd80b5ab361", - "version" : "10.28.0" + "revision" : "c2d59acf17a8ba7ed80a763593c67c9c7c006ad1", + "version" : "12.5.0" } }, { @@ -87,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleDataTransport.git", "state" : { - "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", - "version" : "9.4.0" + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" } }, { @@ -96,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleUtilities.git", "state" : { - "revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6", - "version" : "7.13.3" + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" } }, { @@ -105,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/grpc-binary.git", "state" : { - "revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359", - "version" : "1.62.2" + "revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6", + "version" : "1.69.1" } }, { @@ -123,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/interop-ios-for-google-sdks.git", "state" : { - "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", - "version" : "100.0.0" + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" } }, { @@ -132,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/onevcat/Kingfisher.git", "state" : { - "revision" : "2ef543ee21d63734e1c004ad6c870255e8716c50", - "version" : "7.12.0" + "revision" : "d30a5fad881137e2267f96a8e3fc35c58999bb94", + "version" : "8.6.2" } }, { @@ -168,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SDWebImage/SDWebImage.git", "state" : { - "revision" : "8a1be70a625683bc04d6903e2935bf23f3c6d609", - "version" : "5.19.7" + "revision" : "2053b120767c42a70bcba21095f34e4cfb54a75d", + "version" : "5.21.3" } }, { @@ -181,22 +162,13 @@ "version" : "1.7.0" } }, - { - "identity" : "sdwebimageswiftui", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SDWebImage/SDWebImageSwiftUI.git", - "state" : { - "revision" : "53573d6dd017e354c0e7d8f1c86b77ef1383c996", - "version" : "2.2.7" - } - }, { "identity" : "sfsafesymbols", "kind" : "remoteSourceControl", "location" : "https://github.com/SFSafeSymbols/SFSafeSymbols", "state" : { - "revision" : "e2e28f4e56e1769c2ec3c61c9355fc64eb7a535a", - "version" : "5.3.0" + "revision" : "e01b3d4f861412f8dcee8d93c417d2c2b0cdfd77", + "version" : "7.0.0" } }, { @@ -209,66 +181,39 @@ } }, { - "identity" : "svgkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SVGKit/SVGKit.git", - "state" : { - "branch" : "3.x", - "revision" : "1b8a3cf210ab4681c18fbb62059bbdbb37dd649a" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections", - "state" : { - "revision" : "9bf03ff58ce34478e66aaee630e491823326fd06", - "version" : "1.1.3" - } - }, - { - "identity" : "swift-http-types", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-http-types", - "state" : { - "revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-log", + "identity" : "swift-protobuf", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log", + "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", - "version" : "1.6.1" + "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", + "version" : "1.28.1" } }, { - "identity" : "swift-openapi-runtime", + "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-openapi-runtime", + "location" : "https://github.com/apple/swift-syntax.git", "state" : { - "revision" : "26e8ae3515d1ff3607e924ac96fc0094775f55e8", - "version" : "1.5.0" + "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", + "version" : "509.1.1" } }, { - "identity" : "swift-openapi-urlsession", + "identity" : "swiftformatplugin", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-openapi-urlsession", + "location" : "https://github.com/weakfl/SwiftFormatPlugin", "state" : { - "revision" : "9bf4c712ad7989d6a91dbe68748b8829a50837e4", - "version" : "1.0.2" + "revision" : "daf7c48b2264b11cc8535aa3649b2d8486bb3b08", + "version" : "0.56.1" } }, { - "identity" : "swift-protobuf", + "identity" : "swiftlintplugin", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", + "location" : "https://github.com/weakfl/SwiftLintPlugin.git", "state" : { - "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", - "version" : "1.28.1" + "revision" : "3e10a982ff8f62ba6a19401380280a26e4c56bef", + "version" : "0.59.1" } }, { @@ -276,10 +221,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SwiftKickMobile/SwiftMessages.git", "state" : { - "revision" : "62e12e138fc3eedf88c7553dd5d98712aa119f40", - "version" : "9.0.9" + "revision" : "c0ff6c65bfc00e6a707957cb7069988c5cde2a30", + "version" : "10.0.2" } } ], - "version" : 3 + "version" : 2 } diff --git a/openHAB/HomeSelectionView.swift b/openHAB/HomeSelectionView.swift index 5147d1682..5637b2847 100644 --- a/openHAB/HomeSelectionView.swift +++ b/openHAB/HomeSelectionView.swift @@ -179,7 +179,9 @@ struct HomeSelectionView: View { } private func loadHomesList() { - homes = Preferences.shared.listStoredHomes() + Task { @MainActor in + homes = await Preferences.shared.listStoredHomes() + } } private func delete(home toDelete: UUID?) { diff --git a/openHABIntents/GetItemStateIntentHandler.swift b/openHABIntents/GetItemStateIntentHandler.swift index 36e86cdfb..57696cce4 100644 --- a/openHABIntents/GetItemStateIntentHandler.swift +++ b/openHABIntents/GetItemStateIntentHandler.swift @@ -25,11 +25,13 @@ class GetItemStateIntentHandler: NSObject, OpenHABGetItemStateIntentHandling { } func provideItemOptionsCollection(for intent: OpenHABGetItemStateIntent, searchTerm: String?) async throws -> INObjectCollection { - await OpenHABIntentHelper.getItemOptions(home: intent.home, searchTerm: searchTerm) + Logger.intentHandling.info("provideItemOptionsCollection called with home: \(intent.home?.identifier ?? "nil"), searchTerm: \(searchTerm ?? "nil")") + return await OpenHABIntentHelper.getItemOptions(home: intent.home, searchTerm: searchTerm) } func provideItemOptionsCollection(for intent: OpenHABGetItemStateIntent) async throws -> INObjectCollection { - await OpenHABIntentHelper.getItemOptions(home: intent.home) + Logger.intentHandling.info("provideItemOptionsCollection called with home: \(intent.home?.identifier ?? "nil")") + return await OpenHABIntentHelper.getItemOptions(home: intent.home) } func confirm(intent: OpenHABGetItemStateIntent) async -> OpenHABGetItemStateIntentResponse { @@ -45,11 +47,16 @@ class GetItemStateIntentHandler: NSObject, OpenHABGetItemStateIntentHandling { ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } - let item = await OpenHABItemCache.instance.getItemUncached(name: itemName, home: homeId) + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + let item = await OpenHABItemCache.instance.getItemUncached(name: itemName, home: actualHomeId) guard let item else { return .failureInvalidItem(itemName) diff --git a/openHABIntents/IntentHandler.swift b/openHABIntents/IntentHandler.swift index 260d9c35e..2b8fb07ae 100644 --- a/openHABIntents/IntentHandler.swift +++ b/openHABIntents/IntentHandler.swift @@ -11,6 +11,7 @@ import Intents import OpenHABCore +import os class IntentHandler: INExtension { override init() { @@ -19,7 +20,10 @@ class IntentHandler: INExtension { Task { @MainActor in // Ensure Preferences initializes on the MainActor to avoid crashes _ = Preferences.shared + let homes = await Preferences.shared.listStoredHomes() + Logger.intentHandling.info("IntentHandler init: Found \(homes.count) stored homes: \(homes)") await OpenHABItemCache.instance.forceCacheReload() + Logger.intentHandling.info("IntentHandler init: Cache reload completed") } } diff --git a/openHABIntents/OpenHABIntentHelper.swift b/openHABIntents/OpenHABIntentHelper.swift index 2460c96b1..ac3ef44fc 100644 --- a/openHABIntents/OpenHABIntentHelper.swift +++ b/openHABIntents/OpenHABIntentHelper.swift @@ -12,18 +12,26 @@ import Foundation import Intents import OpenHABCore +import os -@MainActor public enum OpenHABIntentHelper { static func resolveHome(home: OpenHABHome?, item: String?) async -> OpenHABHomeResolutionResult { if let home, let homeId = home.uuid { // TODO: fuzzy matching / account for potential renaming? // TODO: accept potential mismatches if item name is unique - let homePrefs = Preferences.shared.storedHomes.first { $0.key == homeId } + let storedHomes = await Preferences.shared.storedHomes + let homePrefs = storedHomes.first { $0.key == homeId } if homePrefs != nil { return .success(with: home) } else { - return .unsupported() // given home is not found in preferences + // Fallback to first available home if requested home doesn't exist + Logger.intentHandling.warning("Home \(homeId) not found for resolveHome. Available homes: \(storedHomes.keys)") + guard let resolvedHomeId = await resolveHomeId(homeId), + let homeValue = storedHomes[resolvedHomeId] else { + return .unsupported() + } + let fallbackHome = await OpenHABHome(homeId: homeValue.id, homeName: homeValue.homeName) + return .success(with: fallbackHome) } } else if let item { // try to find the home by home-specific item selection @@ -31,9 +39,13 @@ public enum OpenHABIntentHelper { let homeIdsWithMatchingItems = allItems.map(\.key).filter { uuid in allItems[uuid]?.filtered(by: item).isEmpty != true } - let potentialHomes = homeIdsWithMatchingItems - .compactMap { Preferences.shared.storedHomes[$0] } - .map { OpenHABHome(homeId: $0.id, homeName: $0.homeName) } + let storedHomes = await Preferences.shared.storedHomes + var potentialHomes: [OpenHABHome] = [] + await MainActor.run { + potentialHomes = homeIdsWithMatchingItems + .compactMap { storedHomes[$0] } + .map { OpenHABHome(homeId: $0.id, homeName: $0.homeName) } + } if potentialHomes.count == 1 { return .success(with: potentialHomes[0]) } else { @@ -44,23 +56,88 @@ public enum OpenHABIntentHelper { } } - static func getHomeOptions() -> INObjectCollection { - INObjectCollection(items: Preferences.shared.storedHomes.map { OpenHABHome(homeId: $0.value.id, homeName: $0.value.homeName) }) + static func getHomeOptions() async -> INObjectCollection { + let storedHomes = await Preferences.shared.storedHomes + return await MainActor.run { + INObjectCollection(items: storedHomes.map { OpenHABHome(homeId: $0.value.id, homeName: $0.value.homeName) }) + } } static func getItemOptions(home: OpenHABHome?, searchTerm: String? = nil, itemTypes: [OpenHABItem.ItemType]? = nil) async -> INObjectCollection { + Logger.intentHandling.info("getItemOptions called with home: \(home?.identifier ?? "nil"), searchTerm: \(searchTerm ?? "nil")") let allItems = await getAllItems(home: home) + Logger.intentHandling.info("getAllItems returned \(allItems.count) items") let items = allItems.filtered(by: searchTerm, for: itemTypes) + Logger.intentHandling.info("After filtering: \(items.count) items") return INObjectCollection(items: items.map(\.name).map { $0 as NSString }) } private static func getAllItems(home: OpenHABHome?) async -> [OpenHABItem] { if let home, let homeId = home.uuid { - await OpenHABItemCache.instance.getCachedItems(home: homeId) ?? [] + Logger.intentHandling.info("Getting cached items for specific home: \(homeId)") + + // Validate home exists before trying to load and fallback to first available home if needed + guard let actualHomeId = await resolveHomeId(homeId) else { + return [] + } + + let items = await OpenHABItemCache.instance.getCachedItems(home: actualHomeId) ?? [] + Logger.intentHandling.info("Found \(items.count) items for home \(actualHomeId)") + + // If no items found, try to reload the cache once + if items.isEmpty { + Logger.intentHandling.info("No items found, forcing cache reload for home \(actualHomeId)") + await OpenHABItemCache.instance.forceCacheReload(homes: [actualHomeId]) + let reloadedItems = await OpenHABItemCache.instance.getCachedItems(home: actualHomeId) ?? [] + Logger.intentHandling.info("After reload: Found \(reloadedItems.count) items for home \(actualHomeId)") + return reloadedItems + } + + return items } else { - await OpenHABItemCache.instance.getAllCachedItems().flatMap(\.value) + Logger.intentHandling.info("Getting all cached items from all homes") + let allItems = await OpenHABItemCache.instance.getAllCachedItems() + Logger.intentHandling.info("All cached items: \(allItems.map { ($0.key, $0.value.count) })") + let flatItems = allItems.flatMap(\.value) + Logger.intentHandling.info("Total flattened items: \(flatItems.count)") + + // If no items found, try to reload the cache once + if flatItems.isEmpty { + Logger.intentHandling.info("No items found, forcing full cache reload") + await OpenHABItemCache.instance.forceCacheReload() + let reloadedAllItems = await OpenHABItemCache.instance.getAllCachedItems() + Logger.intentHandling.info("After reload: All cached items: \(reloadedAllItems.map { ($0.key, $0.value.count) })") + let reloadedFlatItems = reloadedAllItems.flatMap(\.value) + Logger.intentHandling.info("After reload: Total flattened items: \(reloadedFlatItems.count)") + return reloadedFlatItems + } + + return flatItems } } + + /// Resolves a home ID with fallback logic for deleted homes. + /// If the requested home exists, returns it. Otherwise, falls back to the first available home (sorted by UUID). + /// - Parameter homeId: The requested home UUID + /// - Returns: The resolved home UUID, or nil if no homes are available + static func resolveHomeId(_ homeId: UUID) async -> UUID? { + let storedHomes = await Preferences.shared.storedHomes + + // If requested home exists, return it + if storedHomes[homeId] != nil { + return homeId + } + + // Fallback to first available home (sorted for deterministic behavior) + Logger.intentHandling.warning("Home \(homeId) not found in handle. Falling back to first available home") + guard let firstHome = storedHomes.keys.sorted(by: { $0.uuidString < $1.uuidString }).first else { + Logger.intentHandling.error("No homes available at all!") + return nil + } + + Logger.intentHandling.info("Falling back to first available home: \(firstHome)") + return firstHome + } } extension OpenHABHome: @unchecked Sendable { diff --git a/openHABIntents/SetColorValueIntentHandler.swift b/openHABIntents/SetColorValueIntentHandler.swift index 5ccc83dc0..c0478cc9b 100644 --- a/openHABIntents/SetColorValueIntentHandler.swift +++ b/openHABIntents/SetColorValueIntentHandler.swift @@ -45,7 +45,12 @@ class SetColorValueIntentHandler: NSObject, OpenHABSetColorValueIntentHandling { ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -66,13 +71,13 @@ class SetColorValueIntentHandler: NSObject, OpenHABSetColorValueIntentHandling { value = "\(hue),\(sat),\(val)" - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: value) + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: value) return .success(value: value, item: itemName) } diff --git a/openHABIntents/SetContactStateValueIntentHandler.swift b/openHABIntents/SetContactStateValueIntentHandler.swift index 5dfda5742..44790711c 100644 --- a/openHABIntents/SetContactStateValueIntentHandler.swift +++ b/openHABIntents/SetContactStateValueIntentHandler.swift @@ -58,7 +58,12 @@ class SetContactStateValueIntentHandler: NSObject, OpenHABSetContactStateValueIn ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -73,13 +78,13 @@ class SetContactStateValueIntentHandler: NSObject, OpenHABSetContactStateValueIn return .failureInvalidAction(state: state, item: itemName) } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: realState) + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: realState) return .success(item: itemName, state: state) } diff --git a/openHABIntents/SetDimmerRollerValueIntentHandler.swift b/openHABIntents/SetDimmerRollerValueIntentHandler.swift index a65885aae..2f4533e60 100644 --- a/openHABIntents/SetDimmerRollerValueIntentHandler.swift +++ b/openHABIntents/SetDimmerRollerValueIntentHandler.swift @@ -45,7 +45,12 @@ class SetDimmerRollerValueIntentHandler: NSObject, OpenHABSetDimmerRollerValueIn ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -59,13 +64,13 @@ class SetDimmerRollerValueIntentHandler: NSObject, OpenHABSetDimmerRollerValueIn return .failureInvalidValue(value, item: itemName) } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: "\(number)") + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: "\(number)") return .success(value: NSNumber(value: number), item: itemName) } diff --git a/openHABIntents/SetNumberValueIntentHandler.swift b/openHABIntents/SetNumberValueIntentHandler.swift index 6b7711d2a..a63499b02 100644 --- a/openHABIntents/SetNumberValueIntentHandler.swift +++ b/openHABIntents/SetNumberValueIntentHandler.swift @@ -45,7 +45,12 @@ class SetNumberValueIntentHandler: NSObject, OpenHABSetNumberValueIntentHandling ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -53,13 +58,13 @@ class SetNumberValueIntentHandler: NSObject, OpenHABSetNumberValueIntentHandling return .failureEmptyValue(item: itemName) } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: value.stringValue) + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: value.stringValue) return .success(value: value, item: itemName) } diff --git a/openHABIntents/SetStringValueIntentHandler.swift b/openHABIntents/SetStringValueIntentHandler.swift index d4db35037..9822e1c1b 100644 --- a/openHABIntents/SetStringValueIntentHandler.swift +++ b/openHABIntents/SetStringValueIntentHandler.swift @@ -45,7 +45,12 @@ class SetStringValueIntentHandler: NSObject, OpenHABSetStringValueIntentHandling ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -53,13 +58,13 @@ class SetStringValueIntentHandler: NSObject, OpenHABSetStringValueIntentHandling return .failureEmptyValue(item: itemName) } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: value) + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: value) return .success(value: value, item: itemName) } diff --git a/openHABIntents/SetSwitchStateIntentHandler.swift b/openHABIntents/SetSwitchStateIntentHandler.swift index ada169379..397824688 100644 --- a/openHABIntents/SetSwitchStateIntentHandler.swift +++ b/openHABIntents/SetSwitchStateIntentHandler.swift @@ -61,7 +61,12 @@ final class SetSwitchStateIntentHandler: NSObject, OpenHABSetSwitchStateIntentHa ) } - guard let homeId = home.uuid, await Preferences.shared.storedHomes[homeId] != nil else { + guard let homeId = home.uuid else { + return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) + } + + // Apply fallback if home doesn't exist + guard let actualHomeId = await OpenHABIntentHelper.resolveHomeId(homeId) else { return .failureInvalidItem(NSLocalizedString("unknownHome", comment: "unknown home")) } @@ -77,13 +82,13 @@ final class SetSwitchStateIntentHandler: NSObject, OpenHABSetSwitchStateIntentHa return .failureInvalidAction(action, item: itemName) } - guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: homeId), !items.isEmpty else { + guard let items = await OpenHABItemCache.instance.getCachedItem(name: itemName, home: actualHomeId), !items.isEmpty else { return .failureInvalidItem(itemName) } let item = items[0] - await OpenHABItemCache.instance.sendCommand(to: item, home: homeId, command: command) + await OpenHABItemCache.instance.sendCommand(to: item, home: actualHomeId, command: command) return .success(action: action, item: itemName) } }