Implement enhanced PowerShell cmdlets with async handler, WebSocket, TLS, and more features

Co-authored-by: chkr1011 <6939810+chkr1011@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-07 10:47:51 +00:00
co-authored by chkr1011
parent 71140d12c7
commit 419d7622d8
7 changed files with 305 additions and 20 deletions
@@ -12,13 +12,15 @@ public class ConnectMqttSessionCmdlet : PSCmdlet
[Parameter]
public string? ClientId { get; set; } = Guid.NewGuid().ToString();
[Parameter(Mandatory = true)]
public required new string Host { get; set; }
[Parameter(Mandatory = true, ParameterSetName = "Tcp")]
[Parameter(Mandatory = true, ParameterSetName = "TcpWithTls")]
public new string? Host { get; set; }
[Parameter]
public string? Password { get; set; }
[Parameter]
[Parameter(ParameterSetName = "Tcp")]
[Parameter(ParameterSetName = "TcpWithTls")]
public int Port { get; set; } = 1883;
[Parameter(Mandatory = true, ValueFromPipeline = true)]
@@ -27,19 +29,90 @@ public class ConnectMqttSessionCmdlet : PSCmdlet
[Parameter]
public string? Username { get; set; }
[Parameter]
[Parameter(ParameterSetName = "TcpWithTls")]
public SwitchParameter UseTls { get; set; }
[Parameter(ParameterSetName = "TcpWithTls")]
public SwitchParameter AllowUntrustedCertificates { get; set; }
[Parameter(Mandatory = true, ParameterSetName = "WebSocket")]
public string? WebSocketUri { get; set; }
[Parameter]
public int KeepAlivePeriod { get; set; } = 15;
[Parameter]
public int Timeout { get; set; } = 10;
[Parameter]
public string? WillTopic { get; set; }
[Parameter]
public string? WillPayload { get; set; }
[Parameter]
public int WillQoS { get; set; } = 0;
[Parameter]
public SwitchParameter WillRetain { get; set; }
protected override void ProcessRecord()
{
var clientOptionsBuilder = new MqttClientOptionsBuilder();
clientOptionsBuilder.WithTcpServer(Host, Port);
// Configure transport
if (ParameterSetName == "WebSocket")
{
clientOptionsBuilder.WithWebSocketServer(o => o.WithUri(WebSocketUri!));
}
else
{
clientOptionsBuilder.WithTcpServer(Host!, Port);
if (UseTls)
{
clientOptionsBuilder.WithTlsOptions(o =>
{
if (AllowUntrustedCertificates)
{
o.WithCertificateValidationHandler(_ => true);
}
});
}
}
// Configure authentication
if (Username != null)
{
clientOptionsBuilder.WithCredentials(Username, Password);
}
// Configure session
clientOptionsBuilder.WithCleanSession(CleanSession);
if (!string.IsNullOrEmpty(ClientId))
{
clientOptionsBuilder.WithClientId(ClientId);
}
// Configure keep alive
clientOptionsBuilder.WithKeepAlivePeriod(TimeSpan.FromSeconds(KeepAlivePeriod));
clientOptionsBuilder.WithTimeout(TimeSpan.FromSeconds(Timeout));
// Configure Will message
if (!string.IsNullOrEmpty(WillTopic))
{
clientOptionsBuilder.WithWillTopic(WillTopic);
if (!string.IsNullOrEmpty(WillPayload))
{
clientOptionsBuilder.WithWillPayload(System.Text.Encoding.UTF8.GetBytes(WillPayload));
}
clientOptionsBuilder.WithWillQualityOfServiceLevel((Protocol.MqttQualityOfServiceLevel)WillQoS);
clientOptionsBuilder.WithWillRetain(WillRetain);
}
var response = Session.GetClient().ConnectAsync(clientOptionsBuilder.Build()).GetAwaiter().GetResult();
WriteObject(response);
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Management.Automation;
namespace MQTTnet.PowerShell.Cmdlets;
[Cmdlet(VerbsCommon.Get, "MqttSessionStatus")]
[OutputType(typeof(MqttClientConnectionStatus))]
public class GetMqttSessionStatusCmdlet : PSCmdlet
{
[Parameter(Mandatory = true, ValueFromPipeline = true)]
public required PsMqttSession Session { get; set; }
protected override void ProcessRecord()
{
var client = Session.GetClient();
var status = new
{
IsConnected = client.IsConnected,
Options = client.Options
};
WriteObject(status);
}
}
@@ -1,6 +1,8 @@
using System.Collections;
using System.Management.Automation;
using System.Text;
using MQTTnet.Protocol;
using MQTTnet.Packets;
namespace MQTTnet.PowerShell.Cmdlets;
@@ -22,29 +24,44 @@ public class PublishMqttMessageCmdlet : PSCmdlet
[Parameter(Mandatory = true)]
public required string Topic { get; set; }
[Parameter]
public string? ContentType { get; set; }
[Parameter]
public string? ResponseTopic { get; set; }
[Parameter]
public ushort TopicAlias { get; set; }
[Parameter]
public uint MessageExpiryInterval { get; set; }
[Parameter]
public Hashtable? UserProperties { get; set; }
protected override void ProcessRecord()
{
// if (Session == null || !Session.IsConnected)
// throw new InvalidOperationException("Session not connected.");
var msg = new MqttApplicationMessageBuilder().WithTopic(Topic)
var msgBuilder = new MqttApplicationMessageBuilder()
.WithTopic(Topic)
.WithPayload(Encoding.UTF8.GetBytes(Payload ?? string.Empty))
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)QoS)
.WithRetainFlag(Retain)
.WithContentType(ContentType)
.WithResponseTopic(ResponseTopic)
.WithTopicAlias(TopicAlias)
.WithMessageExpiryInterval(MessageExpiryInterval)
.Build();
.WithMessageExpiryInterval(MessageExpiryInterval);
// Add user properties if provided
if (UserProperties != null && UserProperties.Count > 0)
{
foreach (var key in UserProperties.Keys)
{
var value = UserProperties[key]?.ToString() ?? string.Empty;
msgBuilder.WithUserProperty(key.ToString()!, value);
}
}
var msg = msgBuilder.Build();
var response = Session.GetClient().PublishAsync(msg).GetAwaiter().GetResult();
WriteObject(response);
@@ -14,10 +14,43 @@ public class RegisterMqttMessageHandlerCmdlet : PSCmdlet
protected override void ProcessRecord()
{
EventHandler<PsMqttMessage> handler = (s, e) =>
// Capture the ScriptBlock and current default runspace (available during cmdlet execution)
var scriptBlock = Action ?? throw new ArgumentNullException(nameof(Action));
var capturedRunspace = Runspace.DefaultRunspace ?? throw new InvalidOperationException("No default runspace available during cmdlet execution.");
var capturedHost = Host;
EventHandler<PsMqttMessage> handler = (sender, e) =>
{
//throw new NotImplementedException();
//InvokeCommand.InvokeScript(Action, false, PipelineResultTypes.Output, null, new object[] { e.Topic, e.Payload });
// Run asynchronously to avoid blocking the event thread
Task.Run(() =>
{
// Temporarily set the default runspace for this thread
var originalRunspace = Runspace.DefaultRunspace;
Runspace.DefaultRunspace = capturedRunspace;
try
{
// Invoke the ScriptBlock directly with arguments (returns Collection<PSObject>)
var results = scriptBlock.Invoke(e.Topic, e.Payload, e); // Pass topic, payload, and full message
// Handle output manually (e.g., write to host/console)
foreach (var result in results)
{
capturedHost.UI.WriteLine(result?.ToString() ?? string.Empty);
}
}
catch (Exception ex)
{
// Handle errors by writing to host error stream
capturedHost.UI.WriteErrorLine($"MQTT handler error: {ex.Message}");
// Optionally: capturedHost.UI.WriteVerboseLine(ex.StackTrace); for more details
}
finally
{
// Restore the original default runspace for this thread
Runspace.DefaultRunspace = originalRunspace;
}
});
};
Session.MessageReceived += handler;
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Management.Automation;
namespace MQTTnet.PowerShell.Cmdlets;
[Cmdlet(VerbsLifecycle.Unregister, "MqttMessageHandler")]
public class UnregisterMqttMessageHandlerCmdlet : PSCmdlet
{
[Parameter(Mandatory = true, ValueFromPipeline = true)]
public required PsMqttSession Session { get; set; }
protected override void ProcessRecord()
{
// Clear all event handlers
Session.ClearMessageHandlers();
WriteObject($"All message handlers cleared for MQTT session.");
}
}