Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #38

Merged
merged 5 commits into from
Mar 3, 2024
Merged

Dev #38

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 114 additions & 76 deletions API/MqttClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,69 +34,101 @@ public bool IsAttemptingConnection
get { return _isAttemptingConnection; }
private set { _isAttemptingConnection = value; }
}
public MqttClientWrapper(string clientId, string mqttBroker, string mqttPort, string username, string password, bool UseTLS, bool IgnoreCertificateErrors)

[Obsolete]
public MqttClientWrapper(string clientId, string mqttBroker, string mqttPort, string username, string password, bool useTLS, bool ignoreCertificateErrors, bool useWebsockets)
{
var factory = new MqttFactory();
_mqttClient = factory.CreateMqttClient() as MqttClient;
try
{
var factory = new MqttFactory();
_mqttClient = (MqttClient?)factory.CreateMqttClient();

int mqttportInt;
int.TryParse(mqttPort, out mqttportInt);
if (mqttportInt == 0) mqttportInt = 1883;
if (!int.TryParse(mqttPort, out int mqttportInt))
{
mqttportInt = 1883; // Default MQTT port
Log.Warning($"Invalid MQTT port provided, defaulting to {mqttportInt}");
}

var mqttClientOptionsBuilder = new MqttClientOptionsBuilder()
.WithClientId(clientId)
.WithCredentials(username, password)
.WithCleanSession();
var mqttClientOptionsBuilder = new MqttClientOptionsBuilder()
.WithClientId(clientId)
.WithCredentials(username, password)
.WithCleanSession();

// If useTls is true or the port is 8883, configure the client to use TLS.
if (UseTLS || mqttportInt == 8883)
{
var untrusted = IgnoreCertificateErrors;
// Configure TLS options
mqttClientOptionsBuilder.WithTcpServer(mqttBroker, mqttportInt)
string protocol = useWebsockets ? "ws" : "tcp";
string connectionType = useTLS ? "with TLS" : "without TLS";

if (useWebsockets)
{
string websocketUri = useTLS ? $"wss://{mqttBroker}:{mqttportInt}" : $"ws://{mqttBroker}:{mqttportInt}";
mqttClientOptionsBuilder.WithWebSocketServer(websocketUri);
Log.Information($"Configuring MQTT client for WebSocket {connectionType} connection to {websocketUri}");
}
else
{
mqttClientOptionsBuilder.WithTcpServer(mqttBroker, mqttportInt);
Log.Information($"Configuring MQTT client for TCP {connectionType} connection to {mqttBroker}:{mqttportInt}");
}

.WithTls(new MqttClientOptionsBuilderTlsParameters
if (useTLS)
{
mqttClientOptionsBuilder.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
AllowUntrustedCertificates = untrusted,
IgnoreCertificateChainErrors = untrusted,
IgnoreCertificateRevocationErrors = untrusted,
AllowUntrustedCertificates = ignoreCertificateErrors,
IgnoreCertificateChainErrors = ignoreCertificateErrors,
IgnoreCertificateRevocationErrors = ignoreCertificateErrors,
CertificateValidationHandler = context =>
{
if(IgnoreCertificateErrors)
// Log the certificate subject
Log.Debug("Certificate Subject: {0}", context.Certificate.Subject);

// This assumes you are trying to inspect the certificate directly;
// MQTTnet may not provide a direct IsValid flag or ChainErrors like .NET's X509Chain.
// Instead, you handle validation and log details manually:

bool isValid = true; // You should define the logic to set this based on your validation requirements

// Check for specific conditions, if necessary, such as expiry, issuer, etc.
// For example, if you want to ensure the certificate is issued by a specific entity:
//if (context.Certificate.Issuer != "CN=R3, O=Let's Encrypt, C=US")
//{
// Log.Debug("Unexpected certificate issuer: {0}", context.Certificate.Issuer);
// isValid = false; // Set to false if the issuer is not the expected one
//}

// Log any errors from the SSL policy errors if they exist
if (context.SslPolicyErrors != System.Net.Security.SslPolicyErrors.None)
{
return true;
Log.Debug("SSL policy errors: {0}", context.SslPolicyErrors.ToString());
isValid = false; // Consider invalid if there are any SSL policy errors
}
else

// You can decide to ignore certain errors by setting isValid to true regardless of the checks,
// but be careful as this might introduce security vulnerabilities.
if (ignoreCertificateErrors)
{
return false;
isValid = true; // Ignore certificate errors if your settings dictate
}


return isValid; // Return the result of your checks
}
});


Log.Information($"MQTT Client Created with TLS on port {mqttPort}.");
ConnectionStatusChanged?.Invoke($"MQTT Client Created with TLS");
});
}

_mqttOptions = mqttClientOptionsBuilder.Build();
if (_mqttClient != null)
{
_mqttClient.ApplicationMessageReceivedAsync += OnMessageReceivedAsync;
}
}
else
{
mqttClientOptionsBuilder.WithTcpServer(mqttBroker, mqttportInt);
Log.Information("MQTT Client Created with TCP.");
ConnectionStatusChanged?.Invoke($"MQTT Client Created with TCP");
}

_mqttOptions = mqttClientOptionsBuilder.Build();
if (_mqttClient != null)
catch (Exception ex)
{
_mqttClient.ApplicationMessageReceivedAsync += OnMessageReceivedAsync;
Log.Error(ex, "Failed to initialize MqttClientWrapper");
throw; // Rethrowing the exception to handle it outside or log it as fatal depending on your error handling strategy.
}

}


#endregion Public Constructors

#region Public Events
Expand All @@ -118,7 +150,7 @@ public async Task ConnectAsync()
if (_mqttClient.IsConnected || _isAttemptingConnection)
{
Log.Information("MQTT client is already connected or connection attempt is in progress.");

return;
}

Expand All @@ -134,7 +166,7 @@ public async Task ConnectAsync()
Log.Information("Connected to MQTT broker.");
if (_mqttClient.IsConnected)
ConnectionStatusChanged?.Invoke("MQTT Status: Connected");

break;
}
catch (Exception ex)
Expand Down Expand Up @@ -196,75 +228,81 @@ public static List<string> GetEntityNames(string deviceId)
$"sensor.{deviceId}_issharing",
$"sensor.{deviceId}_hasunreadmessages",
$"switch.{deviceId}_isbackgroundblurred"

};

return entityNames;
}
public async Task PublishAsync(string topic, string payload, bool retain = true)

public async Task PublishAsync(string topic, string payload, bool retain = true)
{
try
{
// Log the topic, payload, and retain flag
Log.Information($"Publishing to topic: {topic}");
Log.Information($"Payload: {payload}");
Log.Information($"Retain flag: {retain}");

// Build the MQTT message
var message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag(retain)
.Build();

// Publish the message using the MQTT client
await _mqttClient.PublishAsync(message);
Log.Information("Publish successful.");
}
catch (Exception ex)
{
// Log any errors that occur during MQTT publish
Log.Information($"Error during MQTT publish: {ex.Message}");
// Depending on the severity, you might want to rethrow the exception or handle it here.
}
}


public async Task SubscribeAsync(string topic, MqttQualityOfServiceLevel qos)
public async Task SubscribeAsync(string topic, MqttQualityOfServiceLevel qos)
{
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(topic).WithQualityOfServiceLevel(qos))
.Build();
try
{
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(topic).WithQualityOfServiceLevel(qos))
.Build();
try
{
await _mqttClient.SubscribeAsync(subscribeOptions);
}
catch (Exception ex)
{
Log.Information($"Error during MQTT subscribe: {ex.Message}");
// Depending on the severity, you might want to rethrow the exception or handle it here.
}
Log.Information("Subscribing." + subscribeOptions);
await _mqttClient.SubscribeAsync(subscribeOptions);
}

#endregion Public Methods

#region Private Methods

private async Task HandleReceivedApplicationMessage(MqttApplicationMessageReceivedEventArgs e)
catch (Exception ex)
{
if (MessageReceived != null)
{
await MessageReceived(e);
Log.Information($"Received message on topic {e.ApplicationMessage.Topic}: {e.ApplicationMessage.ConvertPayloadToString()}");
}
Log.Information($"Error during MQTT subscribe: {ex.Message}");
// Depending on the severity, you might want to rethrow the exception or handle it here.
}
Log.Information("Subscribing." + subscribeOptions);
}

#endregion Public Methods

#region Private Methods

private Task OnMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
private async Task HandleReceivedApplicationMessage(MqttApplicationMessageReceivedEventArgs e)
{
if (MessageReceived != null)
{
await MessageReceived(e);
Log.Information($"Received message on topic {e.ApplicationMessage.Topic}: {e.ApplicationMessage.ConvertPayloadToString()}");
// Trigger the event to notify subscribers
MessageReceived?.Invoke(e);

return Task.CompletedTask;
}
}

#endregion Private Methods
private Task OnMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
{
Log.Information($"Received message on topic {e.ApplicationMessage.Topic}: {e.ApplicationMessage.ConvertPayloadToString()}");
// Trigger the event to notify subscribers
MessageReceived?.Invoke(e);

return Task.CompletedTask;
}

#endregion Private Methods
}
}
1 change: 1 addition & 0 deletions MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<StackPanel Grid.Row="4" Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock Text="MQTT Password:" Width="200" VerticalAlignment="Center"/>
<PasswordBox x:Name="MQTTPasswordBox" PasswordChar="•" Width="300" Margin="10,0,0,0" Style="{DynamicResource MaterialDesignFloatingHintRevealPasswordBox}"/>
<CheckBox x:Name="Websockets" Content="Use Websockets?" Margin="10,0,0,0" Checked="Websockets_Checked" Unchecked="Websockets_Unchecked"/>
</StackPanel>
<StackPanel Grid.Row="5" Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock Text="Sensor Prefix:" Width="200" VerticalAlignment="Center"/>
Expand Down
Loading