From f90f292c426e7da34e4dc64339075bdb63b415eb Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Sun, 6 Oct 2024 10:21:35 -0700 Subject: [PATCH 1/3] Added Save Channel Settings Intent --- Localizable.xcstrings | 12 ++++ Meshtastic.xcodeproj/project.pbxproj | 4 ++ .../SaveChannelSettingsIntent.swift | 62 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 Meshtastic/AppIntents/SaveChannelSettingsIntent.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 11d7c54b..886adafa 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -2782,6 +2782,9 @@ }, "Channel Role" : { + }, + "Channel URL" : { + }, "Channel Utilization %@%% " : { @@ -18850,6 +18853,9 @@ }, "Save" : { + }, + "Save Channel Settings" : { + }, "Save User Config to %@?" : { @@ -20399,6 +20405,9 @@ }, "Supported I2C Connected sensors will be detected automatically, sensors are BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 and SHTC3." : { + }, + "Takes a Meshtastic channel URL and saves the channel settings." : { + }, "tapback" : { "localizations" : { @@ -21099,6 +21108,9 @@ }, "The state of the LED (on/off)" : { + }, + "The URL for the channel settings" : { + }, "There has been no response to a request for device metadata over the admin channel for this node." : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index d9146f08..c49ade1b 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; }; B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; }; B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; }; + BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; }; BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; }; BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; }; BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613842C68703800485544 /* NodePositionIntent.swift */; }; @@ -276,6 +277,7 @@ 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = ""; }; B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = ""; }; B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = ""; }; + BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = ""; }; BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = ""; }; BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = ""; }; BCB613842C68703800485544 /* NodePositionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePositionIntent.swift; sourceTree = ""; }; @@ -590,6 +592,7 @@ BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */, BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */, BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */, + BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */, ); path = AppIntents; sourceTree = ""; @@ -1474,6 +1477,7 @@ DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */, DDAB580F2B0DAFBC00147258 /* LocationEntityExtension.swift in Sources */, B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */, + BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */, D93068D72B8146690066FBC8 /* MessageText.swift in Sources */, DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */, ); diff --git a/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift new file mode 100644 index 00000000..56f21458 --- /dev/null +++ b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift @@ -0,0 +1,62 @@ +// +// SaveChannelSettingsIntent.swift +// Meshtastic +// +// Created by Benjamin Faershtein on 10/6/24. +// + +import Foundation +import AppIntents + +// Define the AppIntent for saving channel settings from a URL +struct SaveChannelSettingsIntent: AppIntent { + // Define a title and description for the intent + static var title: LocalizedStringResource = "Save Channel Settings" + static var description: IntentDescription = "Takes a Meshtastic channel URL and saves the channel settings." + + // Define the input for the intent (the channel URL) + @Parameter(title: "Channel URL", description: "The URL for the channel settings") + var channelUrl: URL + + // Define the function that performs the main logic + func perform() async throws -> some IntentResult { + // Ensure the BLE Manager is connected + if !BLEManager.shared.isConnected { + throw AppIntentErrors.AppIntentError.notConnected + } + + // Ensure the URL contains the expected "meshtastic.org/e/#" structure + if channelUrl.absoluteString.lowercased().contains("meshtastic.org/e/#") { + // Split the URL to get the portion after "#" + let components = channelUrl.absoluteString.components(separatedBy: "#") + + // Add channels flag based on the URL query parameter (if present) + let addChannels = Bool(channelUrl["add"] ?? "false") ?? false + + var channelSettings: String? + + // Extract the Base64 encoded channel settings (after "#") + if let lastComponent = components.last { + channelSettings = lastComponent.components(separatedBy: "?").first // Ignore any query parameters + } + + // If valid channel settings are extracted, attempt to save them + if let channelSettings = channelSettings { + print(channelSettings) + // Call the BLEManager to save the channel settings + let saveResult = BLEManager.shared.saveChannelSet(base64UrlString: channelSettings, addChannels: addChannels) + + if !saveResult { + throw AppIntentErrors.AppIntentError.message("Failed to save the channel settings.") + } + } else { + throw AppIntentErrors.AppIntentError.message("Invalid Channel URL: Unable to extract settings.") + } + + // Return a success result + return .result() + } else { + throw AppIntentErrors.AppIntentError.message("The URL is not a valid Meshtastic channel link.") + } + } +} From 909cd3dc826ea0b4779f67443c3654cd3247b8bd Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Sun, 6 Oct 2024 10:23:12 -0700 Subject: [PATCH 2/3] Remove print --- Meshtastic/AppIntents/SaveChannelSettingsIntent.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift index 56f21458..902a66cc 100644 --- a/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift +++ b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift @@ -42,7 +42,6 @@ struct SaveChannelSettingsIntent: AppIntent { // If valid channel settings are extracted, attempt to save them if let channelSettings = channelSettings { - print(channelSettings) // Call the BLEManager to save the channel settings let saveResult = BLEManager.shared.saveChannelSet(base64UrlString: channelSettings, addChannels: addChannels) From 126142c0a3c704855017c7c6e54e30c00d650fb4 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:14:14 -0800 Subject: [PATCH 3/3] Add Message Node Intent --- Localizable.xcstrings | 24 +++++++--- Meshtastic.xcodeproj/project.pbxproj | 8 ++++ Meshtastic/AppIntents/MessageNodeIntent.swift | 45 +++++++++++++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 Meshtastic/AppIntents/MessageNodeIntent.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 4c41ced3..3ed32241 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -19131,12 +19131,21 @@ }, "Send ${messageContent} to ${channelNumber}" : { + }, + "Send ${messageContent} to ${nodeNumber}" : { + + }, + "Send a Direct Message" : { + }, "Send a Group Message" : { }, "Send a message to a certain meshtastic channel" : { + }, + "Send a message to a certain meshtastic node" : { + }, "Send a position on the primary channel when the user button is triple clicked." : { @@ -19158,6 +19167,9 @@ }, "Sender Interval" : { + }, + "Sensor" : { + }, "Sensor Metrics" : { @@ -21100,13 +21112,12 @@ }, "The state of the LED (on/off)" : { - }, - "The URL for the channel settings" : { - }, "The tertiary public key authorized to send admin messages to this node." : { - + }, + "The URL for the channel settings" : { + }, "There has been no response to a request for device metadata over the admin channel for this node." : { @@ -21885,6 +21896,9 @@ }, "Trace Route was rate limited. You can send a trace route a maximum of once every thirty seconds." : { + }, + "Tracker" : { + }, "Traffic" : { @@ -22719,4 +22733,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 5e28e9a3..1fafb26c 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -32,6 +32,8 @@ 6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; }; B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; }; B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; }; + BC47C2EF2CE0017D008245CA /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */; }; + BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */; }; BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; }; BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; }; BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; }; @@ -278,6 +280,8 @@ 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = ""; }; B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = ""; }; B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = ""; }; + BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = ""; }; + BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = ""; }; BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = ""; }; BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = ""; }; BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = ""; }; @@ -587,6 +591,7 @@ BCB6137F2C6728E700485544 /* AppIntents */ = { isa = PBXGroup; children = ( + BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */, BCB613802C67290800485544 /* SendWaypointIntent.swift */, BCB613822C672A2600485544 /* MessageChannelIntent.swift */, BCB613842C68703800485544 /* NodePositionIntent.swift */, @@ -596,6 +601,7 @@ BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */, BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */, BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */, + BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */, ); path = AppIntents; sourceTree = ""; @@ -1297,6 +1303,7 @@ DDFFA7472B3A7F3C004730DB /* Bundle.swift in Sources */, DD457188293C7E63000C49FB /* BLESignalStrengthIndicator.swift in Sources */, DDA9515C2BC6631200CEA535 /* TelemetryEnums.swift in Sources */, + BC47C2EF2CE0017D008245CA /* MessageNodeIntent.swift in Sources */, DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */, DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */, D93069082B81DF040066FBC8 /* SaveConfigButton.swift in Sources */, @@ -1413,6 +1420,7 @@ DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */, D9BC22DB2B7DE8E2006A37D5 /* TileDownloadStatus.swift in Sources */, DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */, + BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */, DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */, diff --git a/Meshtastic/AppIntents/MessageNodeIntent.swift b/Meshtastic/AppIntents/MessageNodeIntent.swift new file mode 100644 index 00000000..089530bf --- /dev/null +++ b/Meshtastic/AppIntents/MessageNodeIntent.swift @@ -0,0 +1,45 @@ +// +// MessageNodeIntent.swift +// Meshtastic +// +// Created by Benjamin Faershtein on 11/9/24. +// + +import Foundation +import AppIntents + +struct MessageNodeIntent: AppIntent { + static var title: LocalizedStringResource = "Send a Direct Message" + + static var description: IntentDescription = "Send a message to a certain meshtastic node" + + @Parameter(title: "Message") + var messageContent: String + + @Parameter(title: "Node Number") + var nodeNumber: Int + + static var parameterSummary: some ParameterSummary { + Summary("Send \(\.$messageContent) to \(\.$nodeNumber)") + } + func perform() async throws -> some IntentResult { + if !BLEManager.shared.isConnected { + throw AppIntentErrors.AppIntentError.notConnected + } + + // Convert messageContent to data and check its length + guard let messageData = messageContent.data(using: .utf8) else { + throw AppIntentErrors.AppIntentError.message("Failed to encode message content") + } + + if messageData.count > 200 { + throw $messageContent.needsValueError("Message content exceeds 200 bytes.") + } + + if !BLEManager.shared.sendMessage(message: messageContent, toUserNum: Int64(nodeNumber), channel: 0, isEmoji: false, replyID: 0) { + throw AppIntentErrors.AppIntentError.message("Failed to send message") + } + + return .result() + } +}