├── .dockerignore
├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── CommonBin
└── Tak.Client.deps.json
├── Directory.Packages.props
├── LICENSE
├── Meshtastic.Cli
├── .vscode
│ └── settings.json
├── Binders
│ ├── ChannelBinder.cs
│ ├── CommandContextBinder.cs
│ └── DeviceConnectionBinder.cs
├── CommandContext.cs
├── CommandHandlers
│ ├── CaptureCommandHandler.cs
│ ├── ChannelCommandHandler.cs
│ ├── DeviceCommandHandler.cs
│ ├── DfuCommandHandler.cs
│ ├── ExportCommandHandler.cs
│ ├── FactoryResetCommandHandler.cs
│ ├── FileCommandHandler.cs
│ ├── FixedPositionCommandHandler.cs
│ ├── GetCannedMessagesCommandHandler.cs
│ ├── GetCommandHandler.cs
│ ├── ImportCommandHandler.cs
│ ├── InfoCommandHandler.cs
│ ├── ListCommandHandler.cs
│ ├── LiveCommandHandler.cs
│ ├── MetadataCommandHandler.cs
│ ├── MonitorCommandHandler.cs
│ ├── MqttProxyCommandHandler.cs
│ ├── RebootCommandHandler.cs
│ ├── RegisterCommandHandler.cs
│ ├── RemoveNodeCommandHandler.cs
│ ├── RequestTelemetryCommandHandler.cs
│ ├── ResetNodeDbCommandHandler.cs
│ ├── SendTextCommandHandler.cs
│ ├── SendWaypointCommandHandler.cs
│ ├── SetCannedMessagesCommandHandler.cs
│ ├── SetCommandHandler.cs
│ ├── TraceRouteCommandHandler.cs
│ ├── UpdateCommandHandler.cs
│ └── UrlCommandHandler.cs
├── Commands
│ ├── CannedMessagesCommand.cs
│ ├── CaptureCommand.cs
│ ├── ChannelCommand.cs
│ ├── DfuCommand.cs
│ ├── ExportCommand.cs
│ ├── FactoryResetCommand.cs
│ ├── FileCommand.cs
│ ├── FixedPositionCommand.cs
│ ├── GetCommand.cs
│ ├── ImportCommand.cs
│ ├── InfoCommand.cs
│ ├── ListCommand.cs
│ ├── LiveCommand.cs
│ ├── MetadataCommand.cs
│ ├── MonitorCommand.cs
│ ├── MqttProxyCommand.cs
│ ├── RebootCommand.cs
│ ├── RegisterCommand.cs
│ ├── RemoveNodeCommand.cs
│ ├── RequestTelemetryCommand.cs
│ ├── ResetNodeDbCommand.cs
│ ├── SendTextCommand.cs
│ ├── SendWaypointCommand.cs
│ ├── SetCommand.cs
│ ├── TraceRouteCommand.cs
│ ├── UpdateCommand.cs
│ └── UrlCommand.cs
├── DeviceConnectionContext.cs
├── Display
│ └── ProtobufPrinter.cs
├── Enums
│ ├── ChannelOperation.cs
│ ├── ExportOption.cs
│ ├── GetSetOperation.cs
│ └── OutputFormat.cs
├── GlobalSuppressions.cs
├── Logging
│ ├── LoggingExtensions.cs
│ ├── PrettyConsoleLogger.cs
│ ├── PrettyConsoleLoggerConfiguration.cs
│ └── PrettyConsoleLoggerProvider.cs
├── Mappings
│ └── HardwareModelMappings.cs
├── Meshtastic.Cli.csproj
├── Meshtastic.Cli.sln
├── Parsers
│ ├── Parser.cs
│ ├── SettingParser.cs
│ └── UrlParser.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Serialization
│ └── FilteredTypeInspector.cs
├── StyleResources.cs
├── Utilities
│ ├── FirmwarePackageService.cs
│ └── ReleaseZipService.cs
└── appSettings.json
├── Meshtastic.Test
├── CommandHandlers
│ ├── CommandHandlerTestBase.cs
│ └── CommandHandlerTests.cs
├── Commands
│ ├── CannedMessagesCommandTests.cs
│ ├── ChannelCommandTests.cs
│ ├── CommandTestBase.cs
│ ├── FactoryResetCommandTests.cs
│ ├── FakeConsole.cs
│ ├── FakeStdIOWriter.cs
│ ├── FileCommandTests.cs
│ ├── FixedPositionCommandTests.cs
│ ├── GetCommandTests.cs
│ ├── InfoCommandTests.cs
│ ├── ListCommandTests.cs
│ ├── MetadataCommandTests.cs
│ ├── RebootCommandTests.cs
│ ├── ResetNodeDbCommandTests.cs
│ ├── SendTextCommandTests.cs
│ ├── SendWaypointCommandTests.cs
│ ├── SetCommandTests.cs
│ ├── TraceRouteCommandTests.cs
│ └── UrlCommandTests.cs
├── Crypto
│ └── PacketCryptoTests.cs
├── Data
│ ├── AdminMessageFactoryTests.cs
│ ├── DeviceStateContainerTests.cs
│ ├── FakeLogger.cs
│ ├── FromDeviceMessageTests.cs
│ ├── PositionMessageFactoryTests.cs
│ ├── TextMessageFactoryTests.cs
│ ├── ToRadioFactoryTests.cs
│ ├── TraceRouteMessageFactoryTests.cs
│ └── WaypointMessageFactoryTests.cs
├── Extensions
│ ├── DateTimeExtensionsTests.cs
│ ├── DisplayExtensionsTests.cs
│ ├── FromRadioExtensionsTests.cs
│ └── ReflectionExtensionsTests.cs
├── Meshtastic.Test.csproj
├── Parsers
│ ├── SettingParserTests.cs
│ └── UrlParserTests.cs
├── Reflection
│ └── ReflectionExtensionsTests.cs
├── TestCategories.cs
├── Usings.cs
├── Utilities
│ ├── FirmwarePackageServiceTests.cs
│ └── ReleaseZipServiceTests.cs
└── coverlet.runsettings
├── Meshtastic.Virtual.Service
├── Dockerfile
├── Meshtastic.Virtual.Service.csproj
├── Network
│ └── InterfaceUtility.cs
├── Persistance
│ ├── FilePaths.cs
│ ├── FilePersistance.cs
│ ├── IFilePersistance.cs
│ ├── IVirtualStore.cs
│ └── VirtualStore.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── VirtualWorker.cs
├── appsettings.Development.json
├── appsettings.json
├── config.proto
├── module.store
└── node.proto
├── Meshtastic.sln
├── Meshtastic
├── Connections
│ ├── DeviceConnection.cs
│ ├── PacketFraming.cs
│ ├── SerialConnection.cs
│ ├── SimulatedConnection.cs
│ └── TcpConnection.cs
├── Crypto
│ ├── NonceGenerator.cs
│ └── PacketEncryption.cs
├── Data
│ ├── DeviceStateContainer.cs
│ ├── FromRadioMessage.cs
│ └── MessageFactories
│ │ ├── AdminMessageFactory.cs
│ │ ├── PositionMessageFactory.cs
│ │ ├── TelemetryMessageFactory.cs
│ │ ├── TextMessageFactory.cs
│ │ ├── ToRadioMessageFactory.cs
│ │ ├── TraceRouteMessageFactory.cs
│ │ └── WaypointMessageFactory.cs
├── Extensions
│ ├── DateTimeExtensions.cs
│ ├── DisplayExtensions.cs
│ ├── FromRadioExtensions.cs
│ ├── ReflectionExtensions.cs
│ └── ToRadioExtensions.cs
├── Generated
│ ├── Admin.cs
│ ├── Apponly.cs
│ ├── Atak.cs
│ ├── Cannedmessages.cs
│ ├── Channel.cs
│ ├── Clientonly.cs
│ ├── Config.cs
│ ├── ConnectionStatus.cs
│ ├── DeviceUi.cs
│ ├── Interdevice.cs
│ ├── Localonly.cs
│ ├── Mesh.cs
│ ├── ModuleConfig.cs
│ ├── Mqtt.cs
│ ├── Paxcount.cs
│ ├── Portnums.cs
│ ├── Powermon.cs
│ ├── RemoteHardware.cs
│ ├── Rtttl.cs
│ ├── Storeforward.cs
│ ├── Telemetry.cs
│ └── Xmodem.cs
├── Meshtastic.csproj
├── Properties
│ └── launchSettings.json
└── Resources.cs
├── README.md
├── logo.png
├── scripts
├── buildinfo.py
├── bump_version.py
├── readprops.py
├── regen-protos.bat
└── regen-protos.sh
└── version.properties
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: thebentern
4 | open_collective: meshtastic
5 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "protobufs"]
2 | path = protobufs
3 | url = https://github.com/meshtastic/protobufs
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | // Use IntelliSense to find out which attributes exist for C# debugging
6 | // Use hover for the description of the existing attributes
7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
8 | "name": ".NET Core Launch (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/bin/Debug/net7.0/c-sharp.dll",
14 | "args": [],//["info", "--port", "/dev/cu.wchusbserial54350265191"],
15 | "cwd": "${workspaceFolder}",
16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17 | "console": "internalConsole",
18 | "stopAtEntry": false
19 | },
20 | {
21 | "name": ".NET Core Attach",
22 | "type": "coreclr",
23 | "request": "attach"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "dotnet.defaultSolution": "Meshtastic.sln"
3 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/c-sharp.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/c-sharp.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "--project",
36 | "${workspaceFolder}/c-sharp.csproj"
37 | ],
38 | "problemMatcher": "$msCompile"
39 | },
40 | {
41 | "label": "regen protos",
42 | "command": "protoc",
43 | "type": "process",
44 | "args": [
45 | "--proto_path=protobufs",
46 | "--csharp_out=./generated",
47 | "--csharp_opt=base_namespace=Meshtastic.Protobufs",
48 | "protobufs/*.proto"
49 | ],
50 | },
51 | {
52 | "label": "run --noproto",
53 | "command": "dotnet",
54 | "type": "process",
55 | "args": [
56 | "run",
57 | "--project",
58 | "${workspaceFolder}/c-sharp.csproj",
59 | "--noproto"
60 | ],
61 | "problemMatcher": "$msCompile"
62 | },
63 | ]
64 | }
--------------------------------------------------------------------------------
/CommonBin/Tak.Client.deps.json:
--------------------------------------------------------------------------------
1 | {
2 | "runtimeTarget": {
3 | "name": ".NETCoreApp,Version=v7.0",
4 | "signature": ""
5 | },
6 | "compilationOptions": {},
7 | "targets": {
8 | ".NETCoreApp,Version=v7.0": {
9 | "Tak.Client/1.0.0": {
10 | "dependencies": {
11 | "dpp.cot": "1.0.4"
12 | },
13 | "runtime": {
14 | "Tak.Client.dll": {}
15 | }
16 | },
17 | "dpp.cot/1.0.4": {
18 | "dependencies": {
19 | "protobuf-net": "3.0.101"
20 | },
21 | "runtime": {
22 | "lib/net5.0/dpp.cot.dll": {
23 | "assemblyVersion": "1.0.4.0",
24 | "fileVersion": "1.0.4.0"
25 | }
26 | }
27 | },
28 | "protobuf-net/3.0.101": {
29 | "dependencies": {
30 | "protobuf-net.Core": "3.0.101"
31 | },
32 | "runtime": {
33 | "lib/net5.0/protobuf-net.dll": {
34 | "assemblyVersion": "3.0.0.0",
35 | "fileVersion": "3.0.101.59408"
36 | }
37 | }
38 | },
39 | "protobuf-net.Core/3.0.101": {
40 | "runtime": {
41 | "lib/net5.0/protobuf-net.Core.dll": {
42 | "assemblyVersion": "3.0.0.0",
43 | "fileVersion": "3.0.101.59408"
44 | }
45 | }
46 | }
47 | }
48 | },
49 | "libraries": {
50 | "Tak.Client/1.0.0": {
51 | "type": "project",
52 | "serviceable": false,
53 | "sha512": ""
54 | },
55 | "dpp.cot/1.0.4": {
56 | "type": "package",
57 | "serviceable": true,
58 | "sha512": "sha512-HKC33hI9QPEt82gDTmx6b2KOHwQsvtIZySbxrupWKJfQmmqoNhEJfL3ZZqqMsik/X+CEZIKq1sAKd6MftF6Xvw==",
59 | "path": "dpp.cot/1.0.4",
60 | "hashPath": "dpp.cot.1.0.4.nupkg.sha512"
61 | },
62 | "protobuf-net/3.0.101": {
63 | "type": "package",
64 | "serviceable": true,
65 | "sha512": "sha512-mLm8VGfGylp9q+rbJDp3uw0BOeNzfGENecVz/EYGlzq3ss7tfajGXHCMoZm/GxJ9vSn9pcK22UXWZnQ5GJQMjA==",
66 | "path": "protobuf-net/3.0.101",
67 | "hashPath": "protobuf-net.3.0.101.nupkg.sha512"
68 | },
69 | "protobuf-net.Core/3.0.101": {
70 | "type": "package",
71 | "serviceable": true,
72 | "sha512": "sha512-gf9QIF0RlctfjmOjbE6eOyMulkNEsH7tHncjKj/4BTu9tkuTyrH3fv5I+35GnolJqKJ6fh9kYaezvNRL3t75+Q==",
73 | "path": "protobuf-net.core/3.0.101",
74 | "hashPath": "protobuf-net.core.3.0.101.nupkg.sha512"
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "dotnet.defaultSolution": "Meshtastic.Cli.sln"
3 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/Binders/ChannelBinder.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Enums;
2 | using Meshtastic.Protobufs;
3 | using System.CommandLine.Binding;
4 | using System.Diagnostics.CodeAnalysis;
5 |
6 | namespace Meshtastic.Cli.Binders;
7 |
8 | public record ChannelOperationSettings(ChannelOperation Operation, int? Index, string? Name, Channel.Types.Role? Role, string? PSK, bool? UplinkEnabled, bool? DownlinkEnabled);
9 |
10 | [ExcludeFromCodeCoverage(Justification = "Container object")]
11 | public class ChannelBinder : BinderBase
12 | {
13 | private readonly Argument operation;
14 | private readonly Option indexOption;
15 | private readonly Option nameOption;
16 | private readonly Option roleOption;
17 | private readonly Option pskOption;
18 | private readonly Option uplinkOption;
19 | private readonly Option downlinkOption;
20 |
21 | public ChannelBinder(Argument operation,
22 | Option indexOption,
23 | Option nameOption,
24 | Option roleOption,
25 | Option pskOption,
26 | Option uplinkOption,
27 | Option downlinkOption)
28 | {
29 | this.operation = operation;
30 | this.indexOption = indexOption;
31 | this.nameOption = nameOption;
32 | this.roleOption = roleOption;
33 | this.pskOption = pskOption;
34 | this.uplinkOption = uplinkOption;
35 | this.downlinkOption = downlinkOption;
36 | }
37 |
38 | protected override ChannelOperationSettings GetBoundValue(BindingContext bindingContext) =>
39 | new(bindingContext.ParseResult.GetValueForArgument(operation),
40 | bindingContext.ParseResult?.GetValueForOption(indexOption),
41 | bindingContext.ParseResult?.GetValueForOption(nameOption),
42 | bindingContext.ParseResult?.GetValueForOption(roleOption),
43 | bindingContext.ParseResult?.GetValueForOption(pskOption),
44 | bindingContext.ParseResult?.GetValueForOption(uplinkOption),
45 | bindingContext.ParseResult?.GetValueForOption(downlinkOption))
46 | {
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Binders/CommandContextBinder.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Enums;
2 | using Meshtastic.Cli.Logging;
3 | using Microsoft.Extensions.Logging;
4 | using System.CommandLine.Binding;
5 |
6 | namespace Meshtastic.Cli.Binders;
7 |
8 | public class CommandContextBinder : BinderBase
9 | {
10 | private readonly Option logLevel;
11 | private readonly Option outputFormat;
12 | private readonly Option destination;
13 | private readonly Option selectDest;
14 | private readonly Option? channel;
15 |
16 | public CommandContextBinder(Option logLevel,
17 | Option outputFormat,
18 | Option destination,
19 | Option selectDest,
20 | Option? channel = null)
21 | {
22 | this.logLevel = logLevel;
23 | this.outputFormat = outputFormat;
24 | this.destination = destination;
25 | this.selectDest = selectDest;
26 | this.channel = channel;
27 | }
28 |
29 | protected override CommandContext GetBoundValue(BindingContext bindingContext)
30 | {
31 | return new CommandContext(GetLogger(bindingContext),
32 | bindingContext.ParseResult?.GetValueForOption(outputFormat) ?? OutputFormat.PrettyConsole,
33 | bindingContext.ParseResult?.GetValueForOption(destination),
34 | bindingContext.ParseResult?.GetValueForOption(selectDest) ?? false,
35 | channel != null ? bindingContext.ParseResult?.GetValueForOption(channel) : null);
36 | }
37 |
38 | public ILogger GetLogger(BindingContext bindingContext)
39 | {
40 | var level = bindingContext.ParseResult?.GetValueForOption(logLevel) ?? LogLevel.Information;
41 | var output = bindingContext.ParseResult?.GetValueForOption(outputFormat) ?? OutputFormat.PrettyConsole;
42 | var loggerFactory = LoggerFactory.Create(builder =>
43 | {
44 | builder.AddPrettyConsole(new PrettyConsoleLoggerConfiguration()
45 | {
46 | // Don't allow non-console output formats to set chatty loglevels that will corrupt clean ouput
47 | LogLevel = output == OutputFormat.PrettyConsole ? level : LogLevel.Error,
48 | });
49 | builder.SetMinimumLevel(level);
50 | });
51 | return loggerFactory.CreateLogger();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Binders/DeviceConnectionBinder.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine.Binding;
2 |
3 | namespace Meshtastic.Cli.Binders;
4 |
5 | public class DeviceConnectionBinder : BinderBase
6 | {
7 | private readonly Option portOption;
8 | private readonly Option hostOption;
9 |
10 | public DeviceConnectionBinder(Option portOption, Option hostOption)
11 | {
12 | this.portOption = portOption;
13 | this.hostOption = hostOption;
14 | }
15 |
16 | protected override DeviceConnectionContext GetBoundValue(BindingContext bindingContext) =>
17 | new(bindingContext.ParseResult?.GetValueForOption(portOption),
18 | bindingContext.ParseResult?.GetValueForOption(hostOption));
19 | }
20 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandContext.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Enums;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace Meshtastic.Cli;
5 |
6 | public class CommandContext
7 | {
8 | public CommandContext(ILogger logger,
9 | OutputFormat outputFormat,
10 | uint? destination = null,
11 | bool selectDestination = false,
12 | uint? channel = null)
13 | {
14 | Logger = logger;
15 | OutputFormat = outputFormat;
16 | Destination = destination;
17 | SelectDestination = selectDestination;
18 | Channel = channel;
19 | }
20 |
21 | public ILogger Logger { get; }
22 | public OutputFormat OutputFormat { get; }
23 | public uint? Destination { get; }
24 | public bool SelectDestination { get; }
25 | public uint? Channel { get; }
26 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/CaptureCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Display;
4 | using Meshtastic.Protobufs;
5 | using Meshtastic.Extensions;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class CaptureCommandHandler : DeviceCommandHandler
10 | {
11 | public CaptureCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : base(context, commandContext) { }
12 |
13 | public async Task Handle()
14 | {
15 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
16 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
17 | Connection.Disconnect();
18 | return container;
19 | }
20 |
21 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
22 | {
23 | await Connection.ReadFromRadio((fromRadio, container) =>
24 | {
25 | if (fromRadio!.PayloadVariantCase == FromRadio.PayloadVariantOneofCase.Packet) {
26 | var fromRadioDecoded = new FromRadioDecoded(fromRadio)
27 | {
28 | PortNum = fromRadio.Packet?.Decoded?.Portnum,
29 | PayloadSize = fromRadio.Packet?.Decoded?.Payload.Length ?? 0,
30 | ReceivedAt = DateTime.Now,
31 | };
32 | if (fromRadio.GetPayload() != null)
33 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
34 | else if (fromRadio.GetPayload() != null)
35 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
36 | else if (fromRadio.GetPayload() != null)
37 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
38 | else if (fromRadio.GetPayload() != null)
39 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
40 | else if (fromRadio.GetPayload() != null)
41 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
42 | else if (fromRadio.GetPayload() != null)
43 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
44 | else if (fromRadio.GetPayload() != null)
45 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
46 | else if (fromRadio.GetPayload() != null)
47 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
48 | else if (fromRadio.GetPayload() != null)
49 | fromRadioDecoded.DecodedPayload = fromRadio.GetPayload();
50 | }
51 |
52 | return Task.FromResult(false);
53 | });
54 | }
55 | }
56 |
57 | public class FromRadioDecoded
58 | {
59 | public FromRadioDecoded(FromRadio fromRadio)
60 | {
61 | Packet = fromRadio;
62 | }
63 |
64 | public FromRadio Packet { get; set; }
65 | public object? DecodedPayload { get; set; }
66 | public PortNum? PortNum { get; set; }
67 | public int PayloadSize { get; set; }
68 | public int TotalSize { get; set; }
69 | public DateTime ReceivedAt { get; set; }
70 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/DeviceCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Enums;
2 | using Meshtastic.Cli.Parsers;
3 | using Meshtastic.Connections;
4 | using Meshtastic.Data;
5 | using Meshtastic.Data.MessageFactories;
6 | using Meshtastic.Extensions;
7 | using Meshtastic.Protobufs;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace Meshtastic.Cli.CommandHandlers;
11 |
12 | public class DeviceCommandHandler
13 | {
14 | protected readonly DeviceConnectionContext ConnectionContext;
15 | protected readonly DeviceConnection Connection;
16 | protected readonly ToRadioMessageFactory ToRadioMessageFactory;
17 | protected readonly OutputFormat OutputFormat;
18 | protected readonly ILogger Logger;
19 | protected uint? Destination;
20 | protected readonly bool SelectDestination;
21 |
22 | public DeviceCommandHandler(DeviceConnectionContext connectionContext, CommandContext commandContext)
23 | {
24 | ConnectionContext = connectionContext;
25 | Connection = connectionContext.GetDeviceConnection(commandContext.Logger);
26 | ToRadioMessageFactory = new();
27 | OutputFormat = commandContext.OutputFormat;
28 | Logger = commandContext.Logger;
29 | Destination = commandContext.Destination;
30 | SelectDestination = commandContext.SelectDestination;
31 | }
32 |
33 | protected static (SettingParserResult? result, bool isValid) ParseSettingOptions(IEnumerable settings, bool isGetOnly)
34 | {
35 | var parser = new SettingParser(settings);
36 | var parserResult = parser.ParseSettings(isGetOnly);
37 |
38 | if (parserResult.ValidationIssues.Any())
39 | {
40 | foreach (var issue in parserResult.ValidationIssues)
41 | {
42 | AnsiConsole.MarkupLine($"[red]{issue}[/]");
43 | }
44 | return (null, false);
45 | }
46 |
47 | return (parserResult, true);
48 | }
49 |
50 | public static async Task AnyResponseReceived(FromRadio fromRadio, DeviceStateContainer container)
51 | {
52 | await Task.Delay(100);
53 | return true;
54 | }
55 |
56 | public async Task CompleteOnConfigReceived(FromRadio fromRadio, DeviceStateContainer container)
57 | {
58 | if (fromRadio.PayloadVariantCase != FromRadio.PayloadVariantOneofCase.ConfigCompleteId)
59 | return false;
60 |
61 | if (SelectDestination)
62 | {
63 | var selection = new SelectionPrompt()
64 | .Title("Please select a destination node")
65 | .AddChoices(container.Nodes.Select(n => n.Num));
66 | selection.Converter = (num) => container.GetNodeDisplayName(num);
67 | Destination = AnsiConsole.Prompt(selection);
68 | }
69 | await Task.Delay(500);
70 | await OnCompleted(fromRadio, container);
71 | return true;
72 | }
73 |
74 | public virtual async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
75 | {
76 | await Task.CompletedTask;
77 | }
78 |
79 | protected async Task BeginEditSettings(AdminMessageFactory adminMessageFactory)
80 | {
81 | var message = adminMessageFactory.CreateBeginEditSettingsMessage();
82 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(message),
83 | AnyResponseReceived);
84 | Logger.LogInformation($"Starting edit transaction for settings...");
85 | }
86 |
87 | protected async Task CommitEditSettings(AdminMessageFactory adminMessageFactory)
88 | {
89 | var message = adminMessageFactory.CreateCommitEditSettingsMessage();
90 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(message),
91 | AnyResponseReceived);
92 | Logger.LogInformation($"Commit edit transaction for settings...");
93 | }
94 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/DfuCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class DfuCommandHandler : DeviceCommandHandler
9 | {
10 | public DfuCommandHandler(DeviceConnectionContext context,
11 | CommandContext commandContext) : base(context, commandContext)
12 | {
13 | }
14 | public async Task Handle()
15 | {
16 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
17 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
18 | Connection.Disconnect();
19 | return container;
20 | }
21 |
22 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
23 | {
24 | Logger.LogInformation($"Attempting to enter DFU mode (only works on NRF52 devices)...");
25 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
26 | var adminMessage = adminMessageFactory.CreateEnterDfuMessage();
27 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage), AnyResponseReceived);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/FactoryResetCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 | using System.Diagnostics.CodeAnalysis;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | [ExcludeFromCodeCoverage(Justification = "Destructive")]
10 | public class FactoryResetCommandHandler : DeviceCommandHandler
11 | {
12 | public FactoryResetCommandHandler(DeviceConnectionContext context,
13 | CommandContext commandContext) : base(context, commandContext) { }
14 |
15 | public async Task Handle()
16 | {
17 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
18 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
19 | Connection.Disconnect();
20 | return container;
21 | }
22 |
23 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
24 | {
25 | Logger.LogInformation("Factory reseting device...");
26 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
27 | var adminMessage = adminMessageFactory.CreateFactoryResetMessage();
28 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage), AnyResponseReceived);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/FileCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Google.Protobuf;
2 | using Meshtastic.Data;
3 | using Meshtastic.Data.MessageFactories;
4 | using Meshtastic.Extensions;
5 | using Meshtastic.Protobufs;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace Meshtastic.Cli.CommandHandlers;
9 |
10 | public class FileCommandHandler(string path, DeviceConnectionContext context, CommandContext commandContext) : DeviceCommandHandler(context, commandContext)
11 | {
12 | private readonly string path = path;
13 | private readonly MemoryStream memoryStream = new();
14 |
15 | public async Task Handle()
16 | {
17 | var wantConfig = ToRadioMessageFactory.CreateWantConfigMessage();
18 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
19 | Connection.Disconnect();
20 | return container;
21 | }
22 |
23 | public override async Task OnCompleted(FromRadio _, DeviceStateContainer container)
24 | {
25 | var fileRequest = ToRadioMessageFactory.CreateXmodemPacketMessage();
26 | fileRequest.XmodemPacket.Buffer = ByteString.CopyFromUtf8(path);
27 | await Connection.WriteToRadio(fileRequest, async (fromRadio, container) =>
28 | {
29 | var xmodem = fromRadio.GetPayload();
30 | if (xmodem == null)
31 | return false;
32 |
33 | var bufferArray = xmodem.Buffer.ToArray();
34 | await memoryStream.WriteAsync(bufferArray);
35 |
36 | if (xmodem.Control == XModem.Types.Control.Soh)
37 | {
38 | await Task.Delay(100);
39 | var ack = ToRadioMessageFactory.CreateXmodemPacketMessage(XModem.Types.Control.Ack);
40 | await Connection.WriteToRadio(ack);
41 | }
42 | else if (xmodem?.Control == XModem.Types.Control.Eot)
43 | {
44 | var fileName = Path.GetFileName(this.path);
45 | Logger.LogInformation("Retrieved file contents");
46 | Logger.LogInformation($"Writing to {fileName}");
47 |
48 | File.WriteAllBytes(Path.Combine(Environment.CurrentDirectory, fileName), memoryStream.ToArray());
49 | return true;
50 | }
51 | return false;
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/FixedPositionCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Extensions;
4 | using Meshtastic.Protobufs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class FixedPositionCommandHandler(decimal latitude,
10 | decimal longitude,
11 | int altitude,
12 | bool clear,
13 | DeviceConnectionContext context,
14 | CommandContext commandContext) : DeviceCommandHandler(context, commandContext)
15 | {
16 | private readonly decimal latitude = latitude;
17 | private readonly decimal longitude = longitude;
18 | private readonly int altitude = altitude;
19 | private readonly bool clear = clear;
20 | private readonly decimal divisor = new(1e-7);
21 |
22 | public async Task Handle()
23 | {
24 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
25 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
26 | Connection.Disconnect();
27 | return container;
28 | }
29 |
30 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
31 | {
32 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
33 |
34 | var position = new Position()
35 | {
36 | LatitudeI = latitude != 0 ? decimal.ToInt32(latitude / divisor) : 0,
37 | LongitudeI = longitude != 0 ? decimal.ToInt32(longitude / divisor) : 0,
38 | Altitude = altitude,
39 | Time = DateTime.Now.GetUnixTimestamp(),
40 | Timestamp = DateTime.Now.GetUnixTimestamp(),
41 | };
42 |
43 | var adminMessage = clear ? adminMessageFactory.RemovedFixedPositionMessage() : adminMessageFactory.CreateFixedPositionMessage(position);
44 | Logger.LogInformation($"Setting fixed position...");
45 |
46 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage),
47 | (fromRadio, container) =>
48 | {
49 | return Task.FromResult(fromRadio.GetPayload() != null);
50 | });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/GetCannedMessagesCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Extensions;
4 | using Meshtastic.Protobufs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class GetCannedMessagesCommandHandler : DeviceCommandHandler
10 | {
11 | public GetCannedMessagesCommandHandler(DeviceConnectionContext context, CommandContext commandContext) :
12 | base(context, commandContext)
13 | { }
14 |
15 | public async Task Handle()
16 | {
17 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
18 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
19 | Connection.Disconnect();
20 | return container;
21 | }
22 |
23 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
24 | {
25 | Logger.LogInformation("Getting canned messages from device...");
26 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
27 | var adminMessage = adminMessageFactory.CreateGetCannedMessage();
28 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage),
29 | (fromRadio, container) =>
30 | {
31 | var adminMessage = fromRadio.GetPayload();
32 | if (adminMessage?.PayloadVariantCase == AdminMessage.PayloadVariantOneofCase.GetCannedMessageModuleMessagesResponse)
33 | {
34 | Logger.LogInformation($"Canned messages: {adminMessage?.GetCannedMessageModuleMessagesResponse}");
35 | return Task.FromResult(true);
36 | }
37 | return Task.FromResult(false);
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/GetCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Parsers;
2 | using Meshtastic.Data;
3 | using Meshtastic.Data.MessageFactories;
4 | using Meshtastic.Display;
5 | using Meshtastic.Protobufs;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class GetCommandHandler : DeviceCommandHandler
10 | {
11 | public readonly IEnumerable? ParsedSettings;
12 |
13 | public GetCommandHandler(IEnumerable settings,
14 | DeviceConnectionContext context,
15 | CommandContext commandContext) : base(context, commandContext)
16 | {
17 | var (result, isValid) = ParseSettingOptions(settings, isGetOnly: true);
18 | if (!isValid)
19 | return;
20 |
21 | ParsedSettings = result!.ParsedSettings;
22 | }
23 |
24 | public async Task Handle()
25 | {
26 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
27 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
28 | Connection.Disconnect();
29 | return container;
30 | }
31 |
32 | public override Task OnCompleted(FromRadio packet, DeviceStateContainer container)
33 | {
34 | var printer = new ProtobufPrinter(container, OutputFormat);
35 | printer.PrintSettings(ParsedSettings!);
36 | return Task.CompletedTask;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/InfoCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Display;
4 | using Meshtastic.Protobufs;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class InfoCommandHandler : DeviceCommandHandler
9 | {
10 | public InfoCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : base(context, commandContext) { }
11 | public async Task Handle()
12 | {
13 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
14 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
15 | Connection.Disconnect();
16 | return container;
17 | }
18 |
19 | public override Task OnCompleted(FromRadio packet, DeviceStateContainer container)
20 | {
21 | var printer = new ProtobufPrinter(container, OutputFormat);
22 | printer.Print();
23 | return Task.CompletedTask;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/ListCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Connections;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace Meshtastic.Cli.CommandHandlers;
5 |
6 | [ExcludeFromCodeCoverage(Justification = "Requires serial hardware")]
7 | public class ListCommandHandler
8 | {
9 | public static async Task Handle()
10 | {
11 | AnsiConsole.WriteLine("Found the following serial ports:");
12 | foreach (var port in SerialConnection.ListPorts())
13 | AnsiConsole.WriteLine(port);
14 | await Task.CompletedTask;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/LiveCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Display;
4 | using Meshtastic.Protobufs;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class LiveCommandHandler : DeviceCommandHandler
9 | {
10 | public LiveCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : base(context, commandContext) { }
11 |
12 | public async Task Handle()
13 | {
14 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
15 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
16 | Connection.Disconnect();
17 | return container;
18 | }
19 |
20 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
21 | {
22 | var layout = new Layout("Root")
23 | .SplitColumns(
24 | new Layout("Messages"),
25 | new Layout("Right")
26 | .SplitRows(
27 | new Layout("Nodes"),
28 | new Layout("Traffic")));
29 |
30 | await AnsiConsole.Live(layout)
31 | .StartAsync(async ctx =>
32 | {
33 | var printer = new ProtobufPrinter(container, OutputFormat);
34 | UpdateDashboard(layout, printer);
35 |
36 | await Connection.ReadFromRadio((fromRadio, container) =>
37 | {
38 | printer = new ProtobufPrinter(container, OutputFormat);
39 | UpdateDashboard(layout, printer);
40 |
41 | ctx.Refresh();
42 | return Task.FromResult(false);
43 | });
44 | });
45 | }
46 |
47 |
48 | private static void UpdateDashboard(Layout layout, ProtobufPrinter printer)
49 | {
50 | layout["Nodes"].Update(printer.PrintNodesPanel());
51 | layout["Traffic"].Update(printer.PrintTrafficCharts());
52 | layout["Messages"].Update(printer.PrintMessagesPanel());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/MetadataCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Display;
4 | using Meshtastic.Extensions;
5 | using Meshtastic.Protobufs;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace Meshtastic.Cli.CommandHandlers;
9 |
10 | public class MetadataCommandHandler : DeviceCommandHandler
11 | {
12 | public MetadataCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : base(context, commandContext) { }
13 |
14 | public async Task Handle()
15 | {
16 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
17 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
18 | Connection.Disconnect();
19 | return container;
20 | }
21 |
22 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
23 | {
24 | Logger.LogInformation("Getting device metadata...");
25 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
26 | var adminMessage = adminMessageFactory.CreateGetMetadataMessage();
27 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage),
28 | (fromRadio, container) =>
29 | {
30 | var adminMessage = fromRadio.GetPayload();
31 | if (adminMessage?.PayloadVariantCase == AdminMessage.PayloadVariantOneofCase.GetDeviceMetadataResponse)
32 | {
33 | var printer = new ProtobufPrinter(container, OutputFormat);
34 | printer.PrintMetadata(adminMessage!.GetDeviceMetadataResponse);
35 | return Task.FromResult(true);
36 | }
37 | return Task.FromResult(false);
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/MonitorCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace Meshtastic.Cli.CommandHandlers;
4 |
5 | [ExcludeFromCodeCoverage(Justification = "Requires serial hardware")]
6 | public class MonitorCommandHandler : DeviceCommandHandler
7 | {
8 | public MonitorCommandHandler(DeviceConnectionContext context,
9 | CommandContext commandContext) : base(context, commandContext) { }
10 | public async Task Handle()
11 | {
12 | await Connection.Monitor();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/RebootCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class RebootCommandHandler : DeviceCommandHandler
9 | {
10 | private readonly bool isOtaMode = false;
11 | private readonly int seconds = 5;
12 |
13 | public RebootCommandHandler(bool isOtaMode,
14 | int seconds,
15 | DeviceConnectionContext context,
16 | CommandContext commandContext) : base(context, commandContext)
17 | {
18 | this.isOtaMode = isOtaMode;
19 | this.seconds = seconds;
20 | }
21 | public async Task Handle()
22 | {
23 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
24 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
25 | Connection.Disconnect();
26 | return container;
27 | }
28 |
29 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
30 | {
31 | Logger.LogInformation($"Rebooting in {seconds} seconds...");
32 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
33 | var adminMessage = adminMessageFactory.CreateRebootMessage(seconds, isOtaMode);
34 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage), AnyResponseReceived);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/RegisterCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 | using Spectre.Console.Json;
6 | using Newtonsoft.Json;
7 |
8 | namespace Meshtastic.Cli.CommandHandlers;
9 |
10 | public class RegisterCommandHandlerCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : DeviceCommandHandler(context, commandContext)
11 | {
12 | public async Task Handle()
13 | {
14 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
15 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
16 | Connection.Disconnect();
17 | return container;
18 | }
19 |
20 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
21 | {
22 | Logger.LogInformation("Getting registration info...");
23 | var key = container.MyNodeInfo.DeviceId;
24 | var user = container.Nodes.Find(n => n.Num == container.MyNodeInfo.MyNodeNum)?.User;
25 | #pragma warning disable CS0612 // Type or member is obsolete
26 | var macAddress = user?.Macaddr;
27 | #pragma warning restore CS0612 // Type or member is obsolete
28 | if (key == null || key.All(b => b == 0) || user == null || macAddress == null)
29 | {
30 | Logger.LogError("Device does not have a valid key or mac address, and cannot be registered.");
31 | return;
32 | }
33 | var jsonForm = JsonConvert.SerializeObject(new
34 | {
35 | MeshtasticDeviceId = Convert.ToHexString(key.ToByteArray()),
36 | MACAddress = Convert.ToHexString(macAddress.ToByteArray()),
37 | DeviceHardwareId = container.Metadata.HwModel,
38 | container.Metadata.FirmwareVersion,
39 | });
40 |
41 | var json = new JsonText(jsonForm);
42 |
43 | AnsiConsole.Write( new Panel(json)
44 | .Header("Registration Information")
45 | .Collapse()
46 | .RoundedBorder()
47 | .BorderColor(Color.Blue));
48 |
49 | await Task.CompletedTask;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/RemoveNodeCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class RemoveNodeCommandHandler : DeviceCommandHandler
9 | {
10 | private readonly uint nodeNum;
11 |
12 | public RemoveNodeCommandHandler(uint nodeNum, DeviceConnectionContext context,
13 | CommandContext commandContext) : base(context, commandContext)
14 | {
15 | this.nodeNum = nodeNum;
16 | }
17 |
18 | public async Task Handle()
19 | {
20 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
21 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
22 | Connection.Disconnect();
23 | return container;
24 | }
25 |
26 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
27 | {
28 | if (!container.Nodes.Any(n => n.Num == nodeNum))
29 | {
30 | Logger.LogError($"Node with nodenum {nodeNum} not found in device's NodeDB");
31 | return;
32 | }
33 |
34 | Logger.LogInformation("Removing device from NodeDB..");
35 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
36 | var adminMessage = adminMessageFactory.CreateRemoveByNodenumMessage(nodeNum);
37 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage), AnyResponseReceived);
38 | }
39 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/RequestTelemetryCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Meshtastic.Extensions;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class RequestTelemetryCommandHandler : DeviceCommandHandler
10 | {
11 | public RequestTelemetryCommandHandler(DeviceConnectionContext context,
12 | CommandContext commandContext) : base(context, commandContext)
13 | {
14 | }
15 |
16 | public async Task Handle()
17 | {
18 | if (Destination is null)
19 | throw new ApplicationException("Destination must be specified to request telemetry");
20 |
21 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
22 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
23 | Connection.Disconnect();
24 | return container;
25 | }
26 |
27 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
28 | {
29 | var telemetryMessageFactory = new TelemetryMessageFactory(container, Destination);
30 | var message = telemetryMessageFactory.CreateTelemetryPacket();
31 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(message), async (packet, container) =>
32 | {
33 | if (packet.Packet.Decoded.RequestId > 0 && packet.Packet.From == Destination!.Value && packet.GetPayload()?.DeviceMetrics is not null) {
34 | var metrics = packet.GetPayload()?.DeviceMetrics;
35 | Logger.LogInformation($"Received telemetry from destination ({Destination.Value}): \n{metrics}");
36 | }
37 | return await Task.FromResult(false);
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/ResetNodeDbCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class ResetNodeDbCommandHandler : DeviceCommandHandler
9 | {
10 | public ResetNodeDbCommandHandler(DeviceConnectionContext context,
11 | CommandContext commandContext) : base(context, commandContext) { }
12 |
13 | public async Task Handle()
14 | {
15 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
16 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
17 | Connection.Disconnect();
18 | return container;
19 | }
20 |
21 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
22 | {
23 | Logger.LogInformation("Reseting device node db...");
24 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
25 | var adminMessage = adminMessageFactory.CreateNodeDbResetMessage();
26 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage), AnyResponseReceived);
27 | }
28 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/SendTextCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Extensions;
4 | using Meshtastic.Protobufs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class SendTextCommandHandler : DeviceCommandHandler
10 | {
11 | private readonly string message;
12 |
13 | public SendTextCommandHandler(string message, DeviceConnectionContext context, CommandContext commandContext) :
14 | base(context, commandContext)
15 | {
16 | this.message = message;
17 | }
18 | public async Task Handle()
19 | {
20 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
21 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
22 | Connection.Disconnect();
23 | return container;
24 | }
25 |
26 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
27 | {
28 | var textMessageFactory = new TextMessageFactory(container);
29 | var textMessage = textMessageFactory.CreateTextMessagePacket(message);
30 | Logger.LogInformation($"Sending text messagee...");
31 |
32 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(textMessage),
33 | (fromRadio, container) =>
34 | {
35 | var routingResult = fromRadio.GetPayload();
36 | if (routingResult != null && fromRadio.Packet.Priority == MeshPacket.Types.Priority.Ack)
37 | {
38 | if (routingResult.ErrorReason == Routing.Types.Error.None)
39 | Logger.LogInformation("Acknowledged");
40 | else
41 | Logger.LogInformation($"Message delivery failed due to reason: {routingResult.ErrorReason}");
42 |
43 | return Task.FromResult(true);
44 | }
45 |
46 | return Task.FromResult(fromRadio != null);
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/SendWaypointCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 | using System.Text;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class SendWaypointCommandHandler : DeviceCommandHandler
10 | {
11 | private readonly decimal latitude;
12 | private readonly decimal longitude;
13 | private readonly string name;
14 | private readonly string description;
15 | private readonly string icon;
16 | private readonly bool locked;
17 | private readonly decimal divisor = new(1e-7);
18 |
19 | public SendWaypointCommandHandler(decimal latitude,
20 | decimal longitude,
21 | string name,
22 | string description,
23 | string icon,
24 | bool locked,
25 | DeviceConnectionContext context,
26 | CommandContext commandContext) : base(context, commandContext)
27 | {
28 | this.latitude = latitude;
29 | this.longitude = longitude;
30 | this.name = name;
31 | this.description = description;
32 | this.icon = icon;
33 | this.locked = locked;
34 | }
35 |
36 | public async Task Handle()
37 | {
38 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
39 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
40 | Connection.Disconnect();
41 | return container;
42 | }
43 |
44 | public override async Task OnCompleted(FromRadio fromRadio, DeviceStateContainer container)
45 | {
46 | var factory = new WaypointMessageFactory(container, Destination);
47 | var message = factory.CreateWaypointPacket(new Waypoint()
48 | {
49 | Id = (uint)Math.Floor(Random.Shared.Next() * 1e9),
50 | LatitudeI = latitude != 0 ? decimal.ToInt32(latitude / divisor) : 0,
51 | LongitudeI = longitude != 0 ? decimal.ToInt32(longitude / divisor) : 0,
52 | Icon = BitConverter.ToUInt32(Encoding.UTF8.GetBytes(icon)),
53 | Name = name,
54 | Description = description ?? String.Empty,
55 | LockedTo = locked ? container.MyNodeInfo.MyNodeNum : 0,
56 | });
57 | Logger.LogInformation($"Sending waypoint to device...");
58 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(message),
59 | (fromRadio, container) =>
60 | {
61 | if (fromRadio.Packet?.Decoded?.Portnum == PortNum.RoutingApp &&
62 | fromRadio?.Packet?.Priority == MeshPacket.Types.Priority.Ack)
63 | {
64 | var routingResult = Routing.Parser.ParseFrom(fromRadio.Packet.Decoded.Payload);
65 | if (routingResult.ErrorReason == Routing.Types.Error.None)
66 | Logger.LogInformation("Acknowledged");
67 | else
68 | Logger.LogInformation($"Message delivery failed due to reason: {routingResult.ErrorReason}");
69 |
70 | return Task.FromResult(true);
71 | }
72 |
73 | return Task.FromResult(false);
74 | });
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/SetCannedMessagesCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Protobufs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.CommandHandlers;
7 |
8 | public class SetCannedMessagesCommandHandler : DeviceCommandHandler
9 | {
10 | private readonly string messages;
11 |
12 | public SetCannedMessagesCommandHandler(string messages, DeviceConnectionContext context, CommandContext commandContext) :
13 | base(context, commandContext)
14 | {
15 | this.messages = messages;
16 | }
17 | public async Task Handle()
18 | {
19 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
20 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
21 | Connection.Disconnect();
22 | return container;
23 | }
24 |
25 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
26 | {
27 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
28 | await BeginEditSettings(adminMessageFactory);
29 | Logger.LogInformation($"Setting canned messages on device...");
30 |
31 | int index = 0;
32 | do
33 | {
34 | var upperBound = index + 200 > messages.Length ? messages.Length : index + 200;
35 | var setCannedMessage = adminMessageFactory.CreateSetCannedMessage(messages[index..upperBound]);
36 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(setCannedMessage), AnyResponseReceived);
37 |
38 | index = upperBound + 1;
39 | } while (index < (messages.Length - 200));
40 |
41 | await CommitEditSettings(adminMessageFactory);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/SetCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Parsers;
2 | using Meshtastic.Data;
3 | using Meshtastic.Data.MessageFactories;
4 | using Meshtastic.Protobufs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.CommandHandlers;
8 |
9 | public class SetCommandHandler : DeviceCommandHandler
10 | {
11 | public readonly IEnumerable? ParsedSettings;
12 | public SetCommandHandler(IEnumerable settings, DeviceConnectionContext context, CommandContext commandContext) :
13 | base(context, commandContext)
14 | {
15 | var (result, isValid) = ParseSettingOptions(settings, isGetOnly: false);
16 | if (!isValid)
17 | return;
18 |
19 | ParsedSettings = result!.ParsedSettings;
20 | }
21 | public async Task Handle()
22 | {
23 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
24 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
25 | Connection.Disconnect();
26 | return container;
27 | }
28 |
29 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
30 | {
31 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
32 | await BeginEditSettings(adminMessageFactory);
33 |
34 | foreach (var setting in ParsedSettings!)
35 | {
36 | if (setting.Section.ReflectedType?.Name == nameof(container.LocalConfig))
37 | await SetConfig(container, adminMessageFactory, setting);
38 | else
39 | await SetModuleConfig(container, adminMessageFactory, setting);
40 | Logger.LogInformation($"Setting {setting.Section.Name}.{setting.Setting.Name} to {setting.Value?.ToString() ?? string.Empty}...");
41 | }
42 | await CommitEditSettings(adminMessageFactory);
43 | }
44 |
45 | private async Task SetModuleConfig(DeviceStateContainer container, AdminMessageFactory adminMessageFactory, ParsedSetting setting)
46 | {
47 | var instance = setting.Section.GetValue(container.LocalModuleConfig);
48 | setting.Setting.SetValue(instance, setting.Value);
49 | var adminMessage = adminMessageFactory.CreateSetModuleConfigMessage(instance!);
50 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage),
51 | AnyResponseReceived);
52 | }
53 |
54 | private async Task SetConfig(DeviceStateContainer container, AdminMessageFactory adminMessageFactory, ParsedSetting setting)
55 | {
56 | var instance = setting.Section.GetValue(container.LocalConfig);
57 | setting.Setting.SetValue(instance, setting.Value);
58 | var adminMessage = adminMessageFactory.CreateSetConfigMessage(instance!);
59 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(adminMessage),
60 | AnyResponseReceived);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/TraceRouteCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Data;
2 | using Meshtastic.Data.MessageFactories;
3 | using Meshtastic.Display;
4 | using Meshtastic.Extensions;
5 | using Meshtastic.Protobufs;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace Meshtastic.Cli.CommandHandlers;
9 |
10 | public class TraceRouteCommandHandler : DeviceCommandHandler
11 | {
12 | public TraceRouteCommandHandler(DeviceConnectionContext context, CommandContext commandContext) : base(context, commandContext) { }
13 |
14 | public async Task Handle()
15 | {
16 | var wantConfig = new ToRadioMessageFactory().CreateWantConfigMessage();
17 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
18 | Connection.Disconnect();
19 | return container;
20 | }
21 |
22 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
23 | {
24 | Logger.LogInformation($"Tracing route to {container.GetNodeDisplayName(Destination!.Value)}...");
25 | var messageFactory = new TraceRouteMessageFactory(container, Destination);
26 | var message = messageFactory.CreateRouteDiscoveryPacket();
27 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(message),
28 | (fromRadio, container) =>
29 | {
30 | var routeDiscovery = fromRadio.GetPayload();
31 | if (routeDiscovery != null)
32 | {
33 | if (routeDiscovery.Route.Count > 0)
34 | {
35 | var printer = new ProtobufPrinter(container, OutputFormat);
36 | printer.PrintRoute(routeDiscovery.Route);
37 | }
38 | else
39 | Logger.LogWarning("No routes discovered");
40 | return Task.FromResult(true);
41 | }
42 | return Task.FromResult(false);
43 | });
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/CommandHandlers/UrlCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Enums;
2 | using Meshtastic.Cli.Parsers;
3 | using Meshtastic.Data;
4 | using Meshtastic.Data.MessageFactories;
5 | using Meshtastic.Display;
6 | using Meshtastic.Protobufs;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace Meshtastic.Cli.CommandHandlers;
10 |
11 | public class UrlCommandHandler : DeviceCommandHandler
12 | {
13 | private readonly GetSetOperation operation;
14 | private readonly string? url;
15 |
16 | public UrlCommandHandler(GetSetOperation operation, string? url, DeviceConnectionContext context, CommandContext commandContext) :
17 | base(context, commandContext)
18 | {
19 | this.operation = operation;
20 | this.url = url;
21 | }
22 |
23 | public async Task Handle()
24 | {
25 | var wantConfig = ToRadioMessageFactory.CreateWantConfigMessage();
26 | var container = await Connection.WriteToRadio(wantConfig, CompleteOnConfigReceived);
27 | Connection.Disconnect();
28 | return container;
29 | }
30 |
31 | public override async Task OnCompleted(FromRadio packet, DeviceStateContainer container)
32 | {
33 | if (operation == GetSetOperation.Set && url!.Contains("/v/#")) {
34 | await SetContactFromUrl(container);
35 | } else if (operation == GetSetOperation.Set) {
36 | await SetChannelsFromUrl(container);
37 | }
38 | else if (operation == GetSetOperation.Get)
39 | {
40 | var printer = new ProtobufPrinter(container, OutputFormat);
41 | printer.PrintUrl();
42 | }
43 | }
44 | private async Task SetContactFromUrl(DeviceStateContainer container)
45 | {
46 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
47 | var urlParser = new UrlParser(url!);
48 | var contact = urlParser.ParseContact();
49 | Logger.LogInformation($"Sending contact {contact.User.LongName} to device...");
50 | var setContact = adminMessageFactory.CreateAddContactMessage(contact);
51 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(setContact), AnyResponseReceived);
52 | }
53 |
54 | private async Task SetChannelsFromUrl(DeviceStateContainer container)
55 | {
56 | var adminMessageFactory = new AdminMessageFactory(container, Destination);
57 | await BeginEditSettings(adminMessageFactory);
58 | var urlParser = new UrlParser(url!);
59 | var channelSet = urlParser.ParseChannels();
60 | int index = 0;
61 | foreach (var setting in channelSet.Settings)
62 | {
63 | var channel = new Channel
64 | {
65 | Index = index,
66 | Role = index == 0 ? Channel.Types.Role.Primary : Channel.Types.Role.Secondary,
67 | Settings = setting
68 | };
69 | Logger.LogInformation($"Sending channel {index} to device...");
70 | var setChannel = adminMessageFactory.CreateSetChannelMessage(channel);
71 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(setChannel), AnyResponseReceived);
72 | index++;
73 | }
74 | Logger.LogInformation("Sending LoRA config to device...");
75 |
76 | var setLoraConfig = adminMessageFactory.CreateSetConfigMessage(channelSet.LoraConfig);
77 | await Connection.WriteToRadio(ToRadioMessageFactory.CreateMeshPacketMessage(setLoraConfig), AnyResponseReceived);
78 |
79 | await CommitEditSettings(adminMessageFactory);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/CannedMessagesCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class CannedMessagesCommand : Command
9 | {
10 | public CannedMessagesCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) :
12 | base(name, description)
13 | {
14 | var operationArg = new Argument("operation", description: "Get or set canned messages");
15 | operationArg.SetDefaultValue(GetSetOperation.Get);
16 | AddArgument(operationArg);
17 |
18 | var messagesArg = new Argument("messages", description: "Pipe | delimited canned message");
19 | messagesArg.AddValidator(result =>
20 | {
21 | if (result.GetValueForArgument(operationArg) == GetSetOperation.Set)
22 | {
23 | var messages = result.GetValueForArgument(messagesArg);
24 | if (messages == null || !messages.Contains('|'))
25 | result.ErrorMessage = "Must specify pipe delimited messages";
26 | }
27 | });
28 | messagesArg.SetDefaultValue(null);
29 | AddArgument(messagesArg);
30 |
31 |
32 | this.SetHandler(async (operation, messages, context, commandContext) =>
33 | {
34 | if (operation == GetSetOperation.Get)
35 | {
36 | var handler = new GetCannedMessagesCommandHandler(context, commandContext);
37 | await handler.Handle();
38 | }
39 | else
40 | {
41 | var handler = new SetCannedMessagesCommandHandler(messages!, context, commandContext);
42 | await handler.Handle();
43 | }
44 | },
45 | operationArg,
46 | messagesArg,
47 | new DeviceConnectionBinder(port, host),
48 | new CommandContextBinder(log, output, dest, selectDest));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/CaptureCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class CaptureCommand : Command
9 | {
10 | public CaptureCommand(string name, string description, Option port, Option host,
11 | Option output, Option log) : base(name, description)
12 | {
13 | this.SetHandler(async (context, commandContext) =>
14 | {
15 | var handler = new CaptureCommandHandler(context, commandContext);
16 | await handler.Handle();
17 | },
18 | new DeviceConnectionBinder(port, host),
19 | new CommandContextBinder(log, output, new Option("dest") { }, new Option("select-dest") { }));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/ChannelCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Meshtastic.Protobufs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Meshtastic.Cli.Commands;
8 |
9 | public class ChannelCommand : Command
10 | {
11 | public ChannelCommand(string name, string description, Option port, Option host,
12 | Option output, Option log, Option dest, Option selectDest) :
13 | base(name, description)
14 | {
15 | var commandContextBinder = new CommandContextBinder(log, output, dest, selectDest, null);
16 | var operationArgument = new Argument("operation", "The type of channel operation");
17 | operationArgument.AddCompletions(ctx => Enum.GetNames(typeof(ChannelOperation)));
18 |
19 | var indexOption = new Option("--index", description: "Channel index");
20 | indexOption.AddAlias("-i");
21 | indexOption.SetDefaultValue(0);
22 | indexOption.AddValidator(context =>
23 | {
24 | var nonIndexZeroOperation = new[] { ChannelOperation.Disable, ChannelOperation.Enable };
25 | if (context.GetValueForOption(indexOption) < 0 || context.GetValueForOption(indexOption) > 8)
26 | context.ErrorMessage = "Channel index is out of range (0-8)";
27 | else if (nonIndexZeroOperation.Contains(context.GetValueForArgument(operationArgument)) &&
28 | context.GetValueForOption(indexOption) == 0)
29 | {
30 | context.ErrorMessage = "Cannot enable / disable PRIMARY channel";
31 | }
32 | });
33 | var nameOption = new Option("--name", description: "Channel name");
34 | nameOption.AddAlias("-n");
35 | var roleOption = new Option("--role", description: "Channel role");
36 | roleOption.AddAlias("-r");
37 | var pskOption = new Option("--psk", description: "Channel pre-shared key");
38 | pskOption.AddAlias("-p");
39 | var uplinkOption = new Option("--uplink-enabled", description: "Channel uplink enabled");
40 | uplinkOption.AddAlias("-u");
41 | var downlinkOption = new Option("--downlink-enabled", description: "Channel downlink enabled");
42 | downlinkOption.AddAlias("-d");
43 | var channelBinder = new ChannelBinder(operationArgument, indexOption, nameOption, roleOption, pskOption, uplinkOption, downlinkOption);
44 |
45 | AddArgument(operationArgument);
46 | AddOption(indexOption);
47 | AddOption(nameOption);
48 | AddOption(roleOption);
49 | AddOption(pskOption);
50 | AddOption(uplinkOption);
51 | AddOption(downlinkOption);
52 |
53 | this.SetHandler(async (settings, context, commandContext) =>
54 | {
55 | var handler = new ChannelCommandHandler(settings, context, commandContext);
56 | await handler.Handle();
57 | },
58 | channelBinder,
59 | new DeviceConnectionBinder(port, host),
60 | commandContextBinder);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/DfuCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class DfuCommand : Command
9 | {
10 | public DfuCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | this.SetHandler(async (context, commandContext) =>
14 | {
15 | var handler = new DfuCommandHandler(context, commandContext);
16 | await handler.Handle();
17 | },
18 | new DeviceConnectionBinder(port, host),
19 | new CommandContextBinder(log, output, dest, selectDest));
20 | }
21 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/ExportCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 | public class ExportCommand : Command
8 | {
9 | public ExportCommand(string name, string description, Option port, Option host,
10 | Option output, Option log) : base(name, description)
11 | {
12 | var fileOption = new Option("file", "Path to export yaml");
13 | AddOption(fileOption);
14 |
15 | this.SetHandler(async (file, context, commandContext) =>
16 | {
17 | var handler = new ExportCommandHandler(file, context, commandContext);
18 | await handler.Handle();
19 | },
20 | fileOption,
21 | new DeviceConnectionBinder(port, host),
22 | new CommandContextBinder(log, output, new Option("dest"), new Option("select-dest")));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/FactoryResetCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class FactoryResetCommand : Command
9 | {
10 | public FactoryResetCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) :
12 | base(name, description)
13 | {
14 | this.SetHandler(async (context, commandContext) =>
15 | {
16 | var handler = new FactoryResetCommandHandler(context, commandContext);
17 | await handler.Handle();
18 | },
19 | new DeviceConnectionBinder(port, host),
20 | new CommandContextBinder(log, output, dest, selectDest));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/FileCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class FileCommand : Command
9 | {
10 | public FileCommand(string name, string description, Option port, Option host,
11 | Option output, Option log) : base(name, description)
12 | {
13 | var pathArgument = new Argument("path", "The type of url operation");
14 | this.AddArgument(pathArgument);
15 |
16 | this.SetHandler(async (path, context, commandContext) =>
17 | {
18 | var handler = new FileCommandHandler(path, context, commandContext);
19 | await handler.Handle();
20 | },
21 | pathArgument,
22 | new DeviceConnectionBinder(port, host),
23 | new CommandContextBinder(log, output, new Option("dest") { }, new Option("select-dest") { }));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/FixedPositionCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class FixedPositionCommand : Command
9 | {
10 | public FixedPositionCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | var latArg = new Argument("lat", description: "Latitude of the node (decimal format)");
14 | latArg.AddValidator(result =>
15 | {
16 | if (Math.Abs(result.GetValueForArgument(latArg)) > 90)
17 | result.ErrorMessage = "Invalid latitude";
18 | });
19 | AddArgument(latArg);
20 |
21 | var lonArg = new Argument("lon", description: "Longitude of the node (decimal format)");
22 | lonArg.AddValidator(result =>
23 | {
24 | if (Math.Abs(result.GetValueForArgument(lonArg)) > 180)
25 | result.ErrorMessage = "Invalid longitude";
26 | });
27 | AddArgument(lonArg);
28 |
29 | var altArg = new Argument("altitude", description: "Altitude of the node (meters)");
30 | altArg.SetDefaultValue(0);
31 | AddArgument(altArg);
32 |
33 | var clearOption = new Option("clear", description: "Clear fixed position");
34 | clearOption.SetDefaultValue(false);
35 | AddOption(clearOption);
36 |
37 | this.SetHandler(async (lat, lon, alt, clear, context, commandContext) =>
38 | {
39 | var handler = new FixedPositionCommandHandler(lat, lon, alt, clear, context, commandContext);
40 | await handler.Handle();
41 | },
42 | latArg,
43 | lonArg,
44 | altArg,
45 | clearOption,
46 | new DeviceConnectionBinder(port, host),
47 | new CommandContextBinder(log, output, dest, selectDest));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/GetCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class GetCommand : Command
9 | {
10 | public GetCommand(string name, string description, Option> settings, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | this.SetHandler(async (settings, context, commandContext) =>
14 | {
15 | var handler = new GetCommandHandler(settings, context, commandContext);
16 | await handler.Handle();
17 | },
18 | settings,
19 | new DeviceConnectionBinder(port, host),
20 | new CommandContextBinder(log, output, dest, selectDest));
21 | this.AddOption(settings);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/ImportCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 | public class ImportCommand : Command
8 | {
9 | public ImportCommand(string name, string description, Option port, Option host,
10 | Option output, Option log) : base(name, description)
11 | {
12 | var fileOption = new Option("file", "Path to export yaml");
13 | AddOption(fileOption);
14 |
15 | this.SetHandler(async (file, context, commandContext) =>
16 | {
17 | var handler = new ImportCommandHandler(file, context, commandContext);
18 | await handler.Handle();
19 | },
20 | fileOption,
21 | new DeviceConnectionBinder(port, host),
22 | new CommandContextBinder(log, output, new Option("dest"), new Option("select-dest")));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/InfoCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 | public class InfoCommand : Command
8 | {
9 | public InfoCommand(string name, string description, Option port, Option host,
10 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
11 | {
12 | this.SetHandler(async (context, commandContext) =>
13 | {
14 | var handler = new InfoCommandHandler(context, commandContext);
15 | await handler.Handle();
16 | },
17 | new DeviceConnectionBinder(port, host),
18 | new CommandContextBinder(log, output, dest, selectDest));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/ListCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.CommandHandlers;
2 | using Meshtastic.Cli.Enums;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace Meshtastic.Cli.Commands;
6 |
7 | public class ListCommand : Command
8 | {
9 | public ListCommand(string name, string description, Option _, Option __)
10 | : base(name, description)
11 | {
12 | var listCommandHandler = new ListCommandHandler();
13 | this.SetHandler(ListCommandHandler.Handle);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/LiveCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 | public class LiveCommand : Command
8 | {
9 | public LiveCommand(string name, string description, Option port, Option host,
10 | Option output, Option log) : base(name, description)
11 | {
12 | this.SetHandler(async (context, commandContext) =>
13 | {
14 | var handler = new LiveCommandHandler(context, commandContext);
15 | await handler.Handle();
16 | },
17 | new DeviceConnectionBinder(port, host),
18 | new CommandContextBinder(log, output, new Option("dest") { }, new Option("select-dest") { }));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/MetadataCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class MetadataCommand : Command
9 | {
10 | public MetadataCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) :
12 | base(name, description)
13 | {
14 | this.SetHandler(async (context, commandContext) =>
15 | {
16 | var handler = new MetadataCommandHandler(context, commandContext);
17 | await handler.Handle();
18 | },
19 | new DeviceConnectionBinder(port, host),
20 | new CommandContextBinder(log, output, dest, selectDest));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/MonitorCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 | using System.Diagnostics.CodeAnalysis;
6 |
7 | namespace Meshtastic.Cli.Commands;
8 |
9 | [ExcludeFromCodeCoverage(Justification = "Requires serial hardware")]
10 | public class MonitorCommand : Command
11 | {
12 | public MonitorCommand(string name, string description, Option port, Option host,
13 | Option output, Option log) : base(name, description)
14 | {
15 | this.SetHandler(async (context, commandContext) =>
16 | {
17 | var handler = new MonitorCommandHandler(context, commandContext);
18 | await handler.Handle();
19 | },
20 | new DeviceConnectionBinder(port, host),
21 | new CommandContextBinder(log, output, new Option("dest") { }, new Option("selectDest") { }));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/MqttProxyCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 | public class MqttProxyCommand : Command
8 | {
9 | public MqttProxyCommand(string name, string description, Option port, Option host,
10 | Option output, Option log) : base(name, description)
11 | {
12 | this.SetHandler(async (context, commandContext) =>
13 | {
14 | var handler = new MqttProxyCommandHandler(context, commandContext);
15 | await handler.Handle();
16 | },
17 | new DeviceConnectionBinder(port, host),
18 | new CommandContextBinder(log, output, new Option("dest") { }, new Option("select-dest") { }));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/RebootCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class RebootCommand : Command
9 | {
10 | public RebootCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | var otaOption = new Option("ota", "Reboot into OTA update mode");
14 | otaOption.SetDefaultValue(false);
15 |
16 | var secondsArgument = new Argument("seconds", "Number of seconds until reboot");
17 | secondsArgument.SetDefaultValue(5);
18 |
19 | this.SetHandler(async (isOtaMode, seconds, context, commandContext) =>
20 | {
21 | var handler = new RebootCommandHandler(isOtaMode, seconds, context, commandContext);
22 | await handler.Handle();
23 | },
24 | otaOption,
25 | secondsArgument,
26 | new DeviceConnectionBinder(port, host),
27 | new CommandContextBinder(log, output, dest, selectDest));
28 | this.AddOption(otaOption);
29 | this.AddArgument(secondsArgument);
30 | }
31 | }
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/RegisterCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class RegisterCommand : Command
9 | {
10 | public RegisterCommand(string name, string description, Option port, Option host,
11 | Option output, Option log) : base(name, description)
12 | {
13 | this.SetHandler(async (context, commandContext) =>
14 | {
15 | var handler = new RegisterCommandHandlerCommandHandler(context, commandContext);
16 | await handler.Handle();
17 | },
18 | new DeviceConnectionBinder(port, host),
19 | new CommandContextBinder(log, output, new Option("dest"), new Option("select-dest")));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/RemoveNodeCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class RemoveNodeCommand : Command
9 | {
10 | public RemoveNodeCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) :
12 | base(name, description)
13 | {
14 | var nodeNumArgument = new Argument("nodenum", "Nodenum of the node to remove from the device NodeDB");
15 | AddArgument(nodeNumArgument);
16 |
17 | this.SetHandler(async (nodeNum, context, commandContext) =>
18 | {
19 | var handler = new RemoveNodeCommandHandler(nodeNum, context, commandContext);
20 | await handler.Handle();
21 | },
22 | nodeNumArgument,
23 | new DeviceConnectionBinder(port, host),
24 | new CommandContextBinder(log, output, dest, selectDest));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/RequestTelemetryCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class RequestTelemetryCommand : Command
9 | {
10 | public RequestTelemetryCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | this.SetHandler(async (context, commandContext) =>
14 | {
15 | var handler = new RequestTelemetryCommandHandler(context, commandContext);
16 | await handler.Handle();
17 | },
18 | new DeviceConnectionBinder(port, host),
19 | new CommandContextBinder(log, output, dest, selectDest));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/ResetNodeDbCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class ResetNodeDbCommand : Command
9 | {
10 | public ResetNodeDbCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) :
12 | base(name, description)
13 | {
14 | this.SetHandler(async (context, commandContext) =>
15 | {
16 | var handler = new ResetNodeDbCommandHandler(context, commandContext);
17 | await handler.Handle();
18 | },
19 | new DeviceConnectionBinder(port, host),
20 | new CommandContextBinder(log, output, dest, selectDest));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/SendTextCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class SendTextCommand : Command
9 | {
10 | public SendTextCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | var messageArg = new Argument("message", description: "Text message contents");
14 | messageArg.AddValidator(result =>
15 | {
16 | if (String.IsNullOrWhiteSpace(result.GetValueForArgument(messageArg)))
17 | result.ErrorMessage = "Must specify a message";
18 | });
19 | AddArgument(messageArg);
20 |
21 | this.SetHandler(async (message, context, commandContext) =>
22 | {
23 | var handler = new SendTextCommandHandler(message, context, commandContext);
24 | await handler.Handle();
25 | },
26 | messageArg,
27 | new DeviceConnectionBinder(port, host),
28 | new CommandContextBinder(log, output, dest, selectDest));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/SendWaypointCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class SendWaypointCommand : Command
9 | {
10 | public SendWaypointCommand(string name, string description, Option port, Option host,
11 | Option output, Option log, Option dest, Option selectDest) : base(name, description)
12 | {
13 | var latArg = new Argument("lat", description: "Latitude of the node (decimal format)");
14 | latArg.AddValidator(result =>
15 | {
16 | if (Math.Abs(result.GetValueForArgument(latArg)) > 90)
17 | result.ErrorMessage = "Invalid latitude";
18 | });
19 | AddArgument(latArg);
20 |
21 | var lonArg = new Argument("lon", description: "Longitude of the waypoint (decimal format)");
22 | lonArg.AddValidator(result =>
23 | {
24 | if (Math.Abs(result.GetValueForArgument(lonArg)) > 180)
25 | result.ErrorMessage = "Invalid longitude";
26 | });
27 | AddArgument(lonArg);
28 |
29 | var nameOption = new Option("--name", description: "Name for the waypoint");
30 | AddOption(nameOption);
31 |
32 | var descriptionOption = new Option("--description", description: "Description for the waypoint");
33 | AddOption(descriptionOption);
34 |
35 | var iconOption = new Option("--icon", description: "Icon emoji for the waypoint");
36 | iconOption.SetDefaultValue("📍");
37 | iconOption.AddValidator(ctx =>
38 | {
39 | var emoji = ctx.GetValueOrDefault()?.ToString();
40 | if (emoji == null)// || !Regex.IsMatch(emoji, EmojiRegexPattern))
41 | {
42 | ctx.ErrorMessage = "Must specifiy a valid emoji character for waypoint icon";
43 | }
44 | });
45 | AddOption(iconOption);
46 |
47 | var lockedOption = new Option("--locked", description: "Lock the waypoint from updates");
48 | AddOption(lockedOption);
49 |
50 | this.SetHandler(async (lat, lon, name, description, icon, locked, context, commandContext) =>
51 | {
52 | var handler = new SendWaypointCommandHandler(lat, lon, name, description, icon, locked, context, commandContext);
53 | await handler.Handle();
54 | },
55 | latArg,
56 | lonArg,
57 | nameOption,
58 | descriptionOption,
59 | iconOption,
60 | lockedOption,
61 | new DeviceConnectionBinder(port, host),
62 | new CommandContextBinder(log, output, dest, selectDest));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Meshtastic.Cli/Commands/SetCommand.cs:
--------------------------------------------------------------------------------
1 | using Meshtastic.Cli.Binders;
2 | using Meshtastic.Cli.CommandHandlers;
3 | using Meshtastic.Cli.Enums;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Meshtastic.Cli.Commands;
7 |
8 | public class SetCommand : Command
9 | {
10 | public SetCommand(string name, string description, Option> settings, Option port, Option host,
11 | Option output, Option log, Option dest, Option