Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/NetworkOptimizer.Sqm/ScriptGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ private string GenerateSpeedtestScript(Dictionary<string, string> baseline)
? ""
: $" --server-id={_config.PreferredSpeedtestServerId}";
sb.AppendLine("# Run speedtest");
sb.AppendLine($"speedtest_output=$(speedtest --accept-license --format=json --interface=$INTERFACE{serverIdArg})");
sb.AppendLine($"speedtest_output=$(speedtest --accept-license --accept-gdpr --format=json --interface=$INTERFACE{serverIdArg})");
sb.AppendLine();
sb.AppendLine("# Parse download speed (bytes/sec to Mbps)");
sb.AppendLine("download_speed_bytes=$(echo \"$speedtest_output\" | jq .download.bandwidth)");
Expand Down
8 changes: 0 additions & 8 deletions src/NetworkOptimizer.Sqm/SpeedtestIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,6 @@ public bool IsValidResult(SpeedtestResult result)
return true;
}

/// <summary>
/// Generate speedtest command for shell script
/// </summary>
public string GenerateSpeedtestCommand()
{
return $"speedtest --accept-license --format=json --interface={_config.Interface}";
}

/// <summary>
/// Calculate variance from baseline as percentage
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<span class="badge badge-primary">Recommended</span>
</div>
<p>
Run a speed test directly from any web browser using <a href="https://openspeedtest.com" target="_blank">OpenSpeedTest™</a>. Works on all devices.
Run a speed test directly from any web browser using <a href="https://openspeedtest.com" target="_blank" rel="noopener noreferrer">OpenSpeedTest™</a>. Works on all devices.
@if (openSpeedTestUrl?.StartsWith("https://") == true)
{
<text> Location data is collected with each test (with your permission), and results can be seen in the <a href="javascript:void(0)" onclick="document.getElementById('coverage-map').scrollIntoView({behavior: 'smooth'})">Speed / Coverage Map</a> below.</text>
Expand Down Expand Up @@ -148,7 +148,7 @@
<div class="install-header">Install iperf3 on client device:</div>
<div class="install-item"><code>sudo apt install iperf3</code> <span class="text-muted">(Linux)</span></div>
<div class="install-item"><code>brew install iperf3</code> <span class="text-muted">(Mac)</span></div>
<div class="install-item"><a href="https://iperf.fr/iperf-download.php" target="_blank">iperf.fr</a> <span class="text-muted">(Windows)</span></div>
<div class="install-item"><a href="https://iperf.fr/iperf-download.php" target="_blank" rel="noopener noreferrer">iperf.fr</a> <span class="text-muted">(Windows)</span></div>
<div class="install-item">Various clients available on iOS App Store and Google Play</div>
</div>
}
Expand Down
27 changes: 19 additions & 8 deletions src/NetworkOptimizer.Web/Components/Pages/Sqm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -681,15 +681,26 @@
}
else
{
<p class="info-text">
Adaptive SQM uses a dual-mode approach: <strong>Speedtest-based</strong> adjustments run 2x daily
at configured times, while <strong>Ping-based</strong> adjustments run every 5 minutes
for real-time latency optimization.
</p>
@if (deploymentStatus?.IsDeployed != true)
{
<p class="info-text">
Adaptive SQM uses a dual-mode approach: <strong>Speedtest-based</strong> adjustments run 2x daily
at configured times, while <strong>Ping-based</strong> adjustments run every 5 minutes
for real-time latency optimization.
</p>

<div class="alert alert-warning" style="margin: 1rem 0 1.5rem;">
<strong>Disclaimer:</strong> Using Adaptive SQM may affect your ability to receive tech support from Ubiquiti. If tech support ever needs a support file, you may be required to factory reset and restore from a backup. We've tested this on several device combinations, but deploy at your own risk. If you encounter errors or unexpected behavior, use Remove Adaptive SQM to ensure normal gateway performance.
</div>

<div class="alert alert-warning" style="margin: 1rem 0 1.5rem;">
<strong>Disclaimer:</strong> Using Adaptive SQM may affect your ability to receive tech support from Ubiquiti. If tech support ever needs a support file, you may be required to factory reset and restore from a backup. We've tested this on several device combinations, but deploy at your own risk.
</div>
<div class="alert alert-warning" style="margin: 0 0 1.5rem;">
<strong>Ookla Speedtest:</strong> For home/personal use only. By deploying, you accept Ookla's
<a href="https://www.speedtest.net/about/eula" target="_blank" rel="noopener noreferrer">EULA</a>,
<a href="https://www.speedtest.net/about/terms" target="_blank" rel="noopener noreferrer">Terms</a>, and
<a href="https://www.speedtest.net/about/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.
EU users: see <a href="https://www.speedtest.net/gdpr-dpa" target="_blank" rel="noopener noreferrer">GDPR DPA</a>.
</div>
}

<div class="action-buttons">
<button class="btn btn-primary" @onclick="DeploySqm" disabled="@isDeploying">
Expand Down
7 changes: 0 additions & 7 deletions src/NetworkOptimizer.Web/Services/ISqmDeploymentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@ public interface ISqmDeploymentService
/// <returns>A tuple indicating success and a descriptive message.</returns>
Task<(bool success, string message)> TriggerSqmAdjustmentAsync(string wanName);

/// <summary>
/// Trigger a speedtest on the gateway (raw speedtest, not SQM adjustment).
/// </summary>
/// <param name="config">The SQM configuration specifying interface and optional server ID.</param>
/// <returns>A <see cref="SpeedtestResult"/> or null if the speedtest failed.</returns>
Task<SpeedtestResult?> RunSpeedtestAsync(SqmConfig config);

/// <summary>
/// Get SQM status for all WANs by parsing gateway logs.
/// </summary>
Expand Down
7 changes: 0 additions & 7 deletions src/NetworkOptimizer.Web/Services/ISqmService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,4 @@ public interface ISqmService
/// </summary>
/// <returns>True if SQM was successfully disabled, false otherwise.</returns>
Task<bool> DisableSqmAsync();

/// <summary>
/// Run a speedtest on the gateway.
/// </summary>
/// <returns>A <see cref="SpeedtestResult"/> with the speedtest results.</returns>
/// <exception cref="InvalidOperationException">Thrown when the controller is not connected.</exception>
Task<SpeedtestResult> RunSpeedtestAsync();
}
54 changes: 0 additions & 54 deletions src/NetworkOptimizer.Web/Services/SqmDeploymentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,60 +678,6 @@ await RunCommandAsync(
}
}

/// <summary>
/// Trigger a speedtest on the gateway (raw speedtest, not SQM adjustment)
/// </summary>
public async Task<SpeedtestResult?> RunSpeedtestAsync(SqmConfig config)
{
var settings = await GetGatewaySettingsAsync();
if (settings == null || string.IsNullOrEmpty(settings.Host))
{
return null;
}

var device = new DeviceSshConfiguration
{
Host = settings.Host,
SshUsername = settings.Username,
SshPassword = settings.Password,
SshPrivateKeyPath = settings.PrivateKeyPath
};

try
{
var cmd = $"speedtest --accept-license --format=json --interface={config.Interface}";
if (!string.IsNullOrEmpty(config.PreferredSpeedtestServerId))
{
cmd += $" --server-id={config.PreferredSpeedtestServerId}";
}

var result = await RunCommandAsync(cmd);
if (!result.success)
{
_logger.LogError("Speedtest failed: {Error}", result.output);
return null;
}

// Parse JSON result
var json = System.Text.Json.JsonDocument.Parse(result.output);
var root = json.RootElement;

return new SpeedtestResult
{
Timestamp = DateTime.UtcNow,
Download = root.GetProperty("download").GetProperty("bandwidth").GetDouble() * 8 / 1_000_000,
Upload = root.GetProperty("upload").GetProperty("bandwidth").GetDouble() * 8 / 1_000_000,
Latency = root.GetProperty("ping").GetProperty("latency").GetDouble(),
Server = root.GetProperty("server").GetProperty("name").GetString() ?? ""
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to run speedtest");
return null;
}
}

/// <summary>
/// Generate a baseline based on connection type patterns.
/// Uses empirical data patterns scaled to the nominal speed.
Expand Down
25 changes: 0 additions & 25 deletions src/NetworkOptimizer.Web/Services/SqmService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -620,31 +620,6 @@ public async Task<bool> DisableSqmAsync()

return true;
}

public async Task<SpeedtestResult> RunSpeedtestAsync()
{
_logger.LogInformation("Running speedtest");

if (!_connectionService.IsConnected)
{
throw new InvalidOperationException("Cannot run speedtest: controller not connected");
}

// TODO(agent-infrastructure): Trigger speedtest on gateway agent.
// Requires: Agent with iperf3/speedtest-cli installed, endpoint to trigger test,
// and callback to report results. Consider using existing Iperf3SpeedTestService.

await Task.Delay(15000); // Simulate speedtest duration

return new SpeedtestResult
{
Timestamp = DateTime.UtcNow,
Download = 285.4,
Upload = 35.2,
Latency = 12.5,
Server = "Speedtest Server"
};
}
}

public class SqmStatusData
Expand Down
9 changes: 9 additions & 0 deletions src/NetworkOptimizer.Web/wwwroot/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,15 @@ select.form-control {
color: var(--warning-color);
}

.alert-warning a {
color: #fbbf24;
text-decoration: underline;
}

.alert-warning a:hover {
color: #fcd34d;
}

.alert-danger {
background: rgba(239, 68, 68, 0.1);
border: 1px solid var(--danger-color);
Expand Down
21 changes: 0 additions & 21 deletions tests/NetworkOptimizer.Sqm.Tests/SpeedtestIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,27 +395,6 @@ public void IsValidResult_ValidResult_ReturnsTrue()

#endregion

#region GenerateSpeedtestCommand Tests

[Fact]
public void GenerateSpeedtestCommand_GeneratesCorrectCommand()
{
// Arrange
var config = CreateConfig(iface: "eth4");
var integration = new SpeedtestIntegration(config);

// Act
var command = integration.GenerateSpeedtestCommand();

// Assert
command.Should().Contain("speedtest");
command.Should().Contain("--accept-license");
command.Should().Contain("--format=json");
command.Should().Contain("--interface=eth4");
}

#endregion

#region CalculateVariancePercent Tests

[Theory]
Expand Down