Skip to content

Commit

Permalink
Added Shortcuts provider and Shut Down, Reboot, and Factory Reset Int…
Browse files Browse the repository at this point in the history
…ents. Also added logging for intent errors.
  • Loading branch information
RCGV1 committed Aug 26, 2024
1 parent c19b473 commit cfdcc5a
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 12 deletions.
47 changes: 39 additions & 8 deletions Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,9 @@
},
"Are you sure you want to delete this message?" : {

},
"Are you sure you want to factory reset the node?" : {

},
"are.you.sure" : {
"localizations" : {
Expand Down Expand Up @@ -6325,7 +6328,7 @@
"Direct Message Help" : {

},
"Direct messages are using the new public key infrastructure for encryption. Reguires firmware version 2.5 or greater." : {
"Direct messages are using the new public key infrastructure for encryption. Requires firmware version 2.5 or greater." : {

},
"Direct messages are using the shared key for the channel." : {
Expand Down Expand Up @@ -7209,6 +7212,9 @@
},
"Factory Reset" : {

},
"Factory Reset Node" : {

},
"Factory reset your device and app? " : {

Expand All @@ -7227,12 +7233,12 @@
},
"Favorites" : {

},
"Fetch the latest position of a cetain node" : {

},
"Favorites and nodes with recent messages show up at the top of the contact list." : {


},
"Fetch the latest position of a cetain node" : {

},
"Fifteen Minutes" : {

Expand Down Expand Up @@ -14563,11 +14569,15 @@
},
"Message" : {

},
"Message Channel" : {

},
"Message content exceeds 228 bytes." : {

},
"Message Status Options" : {

},
"message.details" : {
"localizations" : {
Expand Down Expand Up @@ -16319,6 +16329,9 @@
}
}
}
},
"Perform a factory reset on the node you are connected to" : {

},
"phone.gps" : {
"localizations" : {
Expand Down Expand Up @@ -17024,6 +17037,9 @@
}
}
}
},
"Reboot Node?" : {

},
"reboot.node" : {
"localizations" : {
Expand Down Expand Up @@ -17389,6 +17405,12 @@
},
"Reset NodeDB" : {

},
"Restart Node" : {

},
"Restart to the node you are connected to" : {

},
"restore" : {

Expand Down Expand Up @@ -19131,13 +19153,16 @@
"Send" : {

},
"Send a channel message" : {
"Send a Channel Message" : {

},
"Send a message to a certain meshtastic channel" : {

},
"Send a waypoint" : {
"Send a shutdown to the node you are connected to" : {

},
"Send a Waypoint" : {

},
"Send ASCII bell with alert message. Useful for triggering external notification on bell." : {
Expand Down Expand Up @@ -19876,6 +19901,12 @@
},
"Show Weather" : {

},
"Shut Down Node" : {

},
"Shut Down Node?" : {

},
"Shutdown Node?" : {

Expand Down
16 changes: 16 additions & 0 deletions Meshtastic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; };
BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613842C68703800485544 /* NodePositionIntent.swift */; };
BCB613872C69A0FB00485544 /* AppIntentErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613862C69A0FB00485544 /* AppIntentErrors.swift */; };
BCE2D3C32C7ADF42008E6199 /* ShutDownNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */; };
BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; };
BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */; };
BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */; };
C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; };
D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D22B8129510066FBC8 /* MessageContextMenuItems.swift */; };
D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D42B812B700066FBC8 /* MessageDestination.swift */; };
Expand Down Expand Up @@ -275,6 +279,10 @@
BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = "<group>"; };
BCB613842C68703800485544 /* NodePositionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePositionIntent.swift; sourceTree = "<group>"; };
BCB613862C69A0FB00485544 /* AppIntentErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentErrors.swift; sourceTree = "<group>"; };
BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShutDownNodeIntent.swift; sourceTree = "<group>"; };
BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestartNodeIntent.swift; sourceTree = "<group>"; };
BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsProvider.swift; sourceTree = "<group>"; };
BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FactoryResetNodeIntent.swift; sourceTree = "<group>"; };
D93068D22B8129510066FBC8 /* MessageContextMenuItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContextMenuItems.swift; sourceTree = "<group>"; };
D93068D42B812B700066FBC8 /* MessageDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDestination.swift; sourceTree = "<group>"; };
D93068D62B8146690066FBC8 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -573,6 +581,10 @@
BCB613822C672A2600485544 /* MessageChannelIntent.swift */,
BCB613842C68703800485544 /* NodePositionIntent.swift */,
BCB613862C69A0FB00485544 /* AppIntentErrors.swift */,
BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */,
BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */,
BCE2D3C82C7C377F008E6199 /* FactoryResetNodeIntent.swift */,
BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */,
);
path = AppIntents;
sourceTree = "<group>";
Expand Down Expand Up @@ -1257,6 +1269,7 @@
25F26B1F2C2F611300C9CD9D /* AppData.swift in Sources */,
25F26B1E2C2F610D00C9CD9D /* Logger.swift in Sources */,
259792252C2F114500AD1659 /* ChannelEntityExtension.swift in Sources */,
BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */,
259792262C2F114500AD1659 /* PositionEntityExtension.swift in Sources */,
259792272C2F114500AD1659 /* TraceRouteEntityExtension.swift in Sources */,
DDDB444829F8A9C900EE2349 /* String.swift in Sources */,
Expand Down Expand Up @@ -1361,6 +1374,7 @@
D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */,
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */,
BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */,
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */,
Expand Down Expand Up @@ -1421,6 +1435,7 @@
BCB613872C69A0FB00485544 /* AppIntentErrors.swift in Sources */,
DD73FD1128750779000852D6 /* PositionLog.swift in Sources */,
DD15E4F52B8BFC8E00654F61 /* PaxCounterLog.swift in Sources */,
BCE2D3C32C7ADF42008E6199 /* ShutDownNodeIntent.swift in Sources */,
25F5D5C22C3F6E4B008036E3 /* AppState.swift in Sources */,
DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */,
DD6F657B2C6EC2900053C113 /* LockLegend.swift in Sources */,
Expand All @@ -1436,6 +1451,7 @@
DDDB26442AAC0206003AFCB7 /* NodeDetail.swift in Sources */,
DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */,
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */,
BCE2D3C92C7C377F008E6199 /* FactoryResetNodeIntent.swift in Sources */,
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */,
DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */,
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */,
Expand Down
9 changes: 7 additions & 2 deletions Meshtastic/AppIntents/AppIntentErrors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import OSLog

class AppIntentErrors {
enum AppIntentError: Swift.Error, CustomLocalizedStringResourceConvertible {
Expand All @@ -14,8 +15,12 @@ class AppIntentErrors {

var localizedStringResource: LocalizedStringResource {
switch self {
case let .message(message): return "Error: \(message)"
case .notConnected: return "No Connected Node"
case let .message(message):
Logger.services.error("App Intent: \(message)")
return "Error: \(message)"
case .notConnected:
Logger.services.error("App Intent: No Connected Node")
return "No Connected Node"
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions Meshtastic/AppIntents/FactoryResetNodeIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// FactoryResetNodeIntent.swift
// Meshtastic
//
// Created by Benjamin Faershtein on 8/25/24.
//

import Foundation
import AppIntents

struct FactoryResetNodeIntent: AppIntent {
static var title: LocalizedStringResource = "Factory Reset Node"
static var description: IntentDescription = "Perform a factory reset on the node you are connected to"

func perform() async throws -> some IntentResult {
// Request user confirmation before performing the factory reset
try await requestConfirmation(result: .result(dialog: "Are you sure you want to factory reset the node?"),confirmationActionName: ConfirmationActionName
.custom(acceptLabel: "Factory Reset", acceptAlternatives: [], denyLabel: "Cancel", denyAlternatives: [], destructive: true))

// Ensure the node is connected
if !BLEManager.shared.isConnected {
throw AppIntentErrors.AppIntentError.notConnected
}

// Safely unwrap the connected node information
if let connectedPeripheralNum = BLEManager.shared.connectedPeripheral?.num,
let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: PersistenceController.shared.container.viewContext),
let fromUser = connectedNode.user,
let toUser = connectedNode.user {

// Attempt to send a factory reset command, throw an error if it fails
if !BLEManager.shared.sendFactoryReset(fromUser: fromUser, toUser: toUser) {
throw AppIntentErrors.AppIntentError.message("Failed to perform factory reset")
}
} else {
throw AppIntentErrors.AppIntentError.message("Failed to retrieve connected node or required data")
}
//
return .result()
}
}
2 changes: 1 addition & 1 deletion Meshtastic/AppIntents/MessageChannelIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
import AppIntents

struct MessageChannelIntent: AppIntent {
static var title: LocalizedStringResource = "Send a channel message"
static var title: LocalizedStringResource = "Send a Channel Message"

static var description: IntentDescription = "Send a message to a certain meshtastic channel"

Expand Down
1 change: 1 addition & 0 deletions Meshtastic/AppIntents/NodePositionIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct NodePositionIntent: AppIntent {
}

let nodeInfo = fetchedNode[0]
nodeInfo.latestEnvironmentMetrics?.batteryLevel
if let latitude = nodeInfo.latestPosition?.coordinate.latitude,
let longitude = nodeInfo.latestPosition?.coordinate.longitude {
let nodeLocation = CLLocation(latitude: latitude, longitude: longitude)
Expand Down
42 changes: 42 additions & 0 deletions Meshtastic/AppIntents/RestartNodeIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// RestartNodeIntent.swift
// Meshtastic
//
// Created by Benjamin Faershtein on 8/24/24.
//

import Foundation
import AppIntents

struct RestartNodeIntent: AppIntent {
static var title: LocalizedStringResource = "Restart Node"

static var description: IntentDescription = "Restart to the node you are connected to"


func perform() async throws -> some IntentResult {

try await requestConfirmation(result: .result(dialog: "Reboot Node?"))

if !BLEManager.shared.isConnected {
throw AppIntentErrors.AppIntentError.notConnected
}
// Safely unwrap the connectedNode using if let
if let connectedPeripheralNum = BLEManager.shared.connectedPeripheral?.num,
let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: PersistenceController.shared.container.viewContext),
let fromUser = connectedNode.user,
let toUser = connectedNode.user,
let adminIndex = connectedNode.myInfo?.adminIndex {

// Attempt to send shutdown, throw an error if it fails
if !BLEManager.shared.sendReboot(fromUser: fromUser, toUser: toUser, adminIndex: adminIndex) {
throw AppIntentErrors.AppIntentError.message("Failed to restart")
}
} else {
throw AppIntentErrors.AppIntentError.message("Failed to retrieve connected node or required data")
}

return .result()
}
}

2 changes: 1 addition & 1 deletion Meshtastic/AppIntents/SendWaypointIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import MeshtasticProtobufs

struct SendWaypointIntent: AppIntent {

static var title = LocalizedStringResource("Send a waypoint")
static var title = LocalizedStringResource("Send a Waypoint")

@Parameter(title: "Name", default: "Dropped Pin")
var nameParameter: String?
Expand Down
37 changes: 37 additions & 0 deletions Meshtastic/AppIntents/ShortcutsProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// ShortcutsProvider.swift
// Meshtastic
//
// Created by Benjamin Faershtein on 8/24/24.
//

import Foundation
import AppIntents

struct ShortcutsProvider: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(intent: ShutDownNodeIntent(),
phrases: ["Shut down node in \(.applicationName)",
"Turn off node in \(.applicationName)",
"Power down node in \(.applicationName)",
"Deactivate node in \(.applicationName)"],
shortTitle: "Shut Down Node",
systemImageName: "power")

AppShortcut(intent: RestartNodeIntent(),
phrases: ["Restart node in \(.applicationName)",
"Reboot node in \(.applicationName)",
"Reset node in \(.applicationName)",
"Start node again in \(.applicationName)"],
shortTitle: "Restart Node",
systemImageName: "arrow.circlepath")

AppShortcut(intent: MessageChannelIntent(),
phrases: ["Message channel in \(.applicationName)",],
shortTitle: "Message Channel",
systemImageName: "message")
}
}



Loading

0 comments on commit cfdcc5a

Please sign in to comment.