mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
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:
co-authored by
chkr1011
parent
71140d12c7
commit
419d7622d8
@@ -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.");
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,18 @@ public sealed class PsMqttSession : IDisposable
|
||||
|
||||
public event EventHandler<PsMqttMessage>? MessageReceived;
|
||||
|
||||
public void ClearMessageHandlers()
|
||||
{
|
||||
// Remove all event subscribers
|
||||
if (MessageReceived != null)
|
||||
{
|
||||
foreach (var handler in MessageReceived.GetInvocationList())
|
||||
{
|
||||
MessageReceived -= (EventHandler<PsMqttMessage>)handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
|
||||
@@ -1,15 +1,117 @@
|
||||
# MQTTnet PowerShell Module Test Script
|
||||
# This script demonstrates the usage of the MQTTnet PowerShell module
|
||||
|
||||
Import-Module ./bin/Debug/net8.0/MQTTnet.PowerShell.dll
|
||||
|
||||
Write-Host "=== MQTTnet PowerShell Module Test ===" -ForegroundColor Cyan
|
||||
|
||||
# Example 1: Basic TCP connection
|
||||
Write-Host "`n1. Testing basic TCP connection..." -ForegroundColor Yellow
|
||||
$session = New-MqttSession
|
||||
|
||||
Connect-MqttSession -Session $session -Host "192.168.1.16"
|
||||
try {
|
||||
Connect-MqttSession -Session $session -Host "broker.hivemq.com" -Port 1883
|
||||
Write-Host "Connected successfully!" -ForegroundColor Green
|
||||
|
||||
# Get session status
|
||||
Write-Host "`n2. Getting session status..." -ForegroundColor Yellow
|
||||
$status = Get-MqttSessionStatus -Session $session
|
||||
Write-Host "IsConnected: $($status.IsConnected)" -ForegroundColor Green
|
||||
|
||||
# Example 2: Publish a simple message
|
||||
Write-Host "`n3. Publishing a simple message..." -ForegroundColor Yellow
|
||||
Publish-MqttMessage -Session $session -Topic "test/powershell" -Payload "Hello from PowerShell!"
|
||||
Write-Host "Message published!" -ForegroundColor Green
|
||||
|
||||
# Example 3: Publish with user properties
|
||||
Write-Host "`n4. Publishing with user properties..." -ForegroundColor Yellow
|
||||
$properties = @{
|
||||
"source" = "PowerShell"
|
||||
"version" = "1.0"
|
||||
}
|
||||
Publish-MqttMessage -Session $session -Topic "test/powershell/advanced" -Payload "Advanced message" -UserProperties $properties -QoS 1
|
||||
Write-Host "Message with properties published!" -ForegroundColor Green
|
||||
|
||||
# Example 4: Subscribe and receive messages
|
||||
Write-Host "`n5. Subscribing to topics..." -ForegroundColor Yellow
|
||||
Subscribe-MqttTopic -Session $session -Topic "test/demo"
|
||||
Write-Host "Subscribed to test/demo" -ForegroundColor Green
|
||||
|
||||
# Example 5: Register a message handler
|
||||
Write-Host "`n6. Registering message handler..." -ForegroundColor Yellow
|
||||
Register-MqttMessageHandler -Session $session -Action {
|
||||
param($topic, $payload, $message)
|
||||
Write-Host "Received message on topic: $topic" -ForegroundColor Cyan
|
||||
Write-Host "Payload: $payload" -ForegroundColor White
|
||||
Write-Host "QoS: $($message.QoS)" -ForegroundColor White
|
||||
}
|
||||
Write-Host "Handler registered!" -ForegroundColor Green
|
||||
|
||||
# Publish a test message that the handler will receive
|
||||
Write-Host "`n7. Publishing test message for handler..." -ForegroundColor Yellow
|
||||
Publish-MqttMessage -Session $session -Topic "test/demo" -Payload "Test message for handler"
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Example 6: Unsubscribe
|
||||
Write-Host "`n8. Unsubscribing from topic..." -ForegroundColor Yellow
|
||||
Unsubscribe-MqttTopic -Session $session -Topic "test/demo"
|
||||
Write-Host "Unsubscribed!" -ForegroundColor Green
|
||||
|
||||
# Example 7: Clean disconnect
|
||||
Write-Host "`n9. Disconnecting..." -ForegroundColor Yellow
|
||||
Disconnect-MqttSession -Session $session
|
||||
Write-Host "Disconnected!" -ForegroundColor Green
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error: $_" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
# Cleanup
|
||||
Remove-MqttSession -Session $session
|
||||
Write-Host "`nSession cleaned up!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Publish-MqttMessage -Session $session -Topic "Test" -Payload "Hello from PowerShell"
|
||||
Write-Host "`n=== WebSocket Connection Example ===" -ForegroundColor Cyan
|
||||
|
||||
Subscribe-MqttTopic -Session $session -Topic "test/demo"
|
||||
# Example 8: WebSocket connection
|
||||
Write-Host "`nTesting WebSocket connection..." -ForegroundColor Yellow
|
||||
$wsSession = New-MqttSession
|
||||
|
||||
Receive-MqttMessage -Session $session -TimeoutSeconds 200
|
||||
try {
|
||||
Connect-MqttSession -Session $wsSession -WebSocketUri "broker.hivemq.com:8000/mqtt"
|
||||
Write-Host "Connected via WebSocket!" -ForegroundColor Green
|
||||
|
||||
Publish-MqttMessage -Session $wsSession -Topic "test/websocket" -Payload "Hello via WebSocket!"
|
||||
Write-Host "Message published via WebSocket!" -ForegroundColor Green
|
||||
|
||||
Disconnect-MqttSession -Session $wsSession
|
||||
}
|
||||
catch {
|
||||
Write-Host "WebSocket Error: $_" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
Remove-MqttSession -Session $wsSession
|
||||
}
|
||||
|
||||
Disconnect-MqttSession -Session $session
|
||||
Remove-MqttSession -Session $session
|
||||
Write-Host "`n=== Connection with Will Message Example ===" -ForegroundColor Cyan
|
||||
|
||||
# Example 9: Connection with Last Will Testament
|
||||
Write-Host "`nTesting connection with Will message..." -ForegroundColor Yellow
|
||||
$willSession = New-MqttSession
|
||||
|
||||
try {
|
||||
Connect-MqttSession -Session $willSession -Host "broker.hivemq.com" `
|
||||
-WillTopic "test/will" -WillPayload "Client disconnected unexpectedly" -WillQoS 1 -WillRetain
|
||||
Write-Host "Connected with Will message configured!" -ForegroundColor Green
|
||||
|
||||
Disconnect-MqttSession -Session $willSession
|
||||
}
|
||||
catch {
|
||||
Write-Host "Will Error: $_" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
Remove-MqttSession -Session $willSession
|
||||
}
|
||||
|
||||
Write-Host "`n=== All tests completed ===" -ForegroundColor Cyan
|
||||
|
||||
|
||||
Reference in New Issue
Block a user