Skip to content

Commit

Permalink
Merge pull request #38 from jimmyeao/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
jimmyeao authored Mar 3, 2024
2 parents 52e2675 + 0a5cc5c commit feb7cad
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 89 deletions.
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

0 comments on commit feb7cad

Please sign in to comment.