Thank you for your interest in contributing! This project maintains high code quality standards and welcomes contributions that follow our established patterns.
- ✅ Build without warnings or errors
- ✅ Follow DRY principle (no code duplication)
- ✅ Use DeviceParserBase helpers for all parsers
- ✅ Follow existing patterns
- ✅ Include appropriate error handling
# Install .NET 10.0 SDK
https://dotnet.microsoft.com/download/dotnet/10.0
# Clone the repository
git clone <repository-url>
cd mDNSDiscovery
# Restore dependencies
dotnet restore
# Build
dotnet build
# Run
cd src/mDNSDiscovery.WebApp
dotnet runRead these documents in order:
- README.md - Overview and features
- PARSER_REFACTORING_GUIDE.md - Parser architecture
- FINAL_REVIEW_REPORT.md - Code quality standards
1. Create the parser file:
Create src/mDNSDiscovery.WebApp/Services/Parsers/YourDeviceParser.cs
2. Use this template:
namespace mDNSDiscovery.WebApp.Services.Parsers;
/// <summary>
/// Parser for YourDevice (_yourservice._tcp)
/// </summary>
public class YourDeviceParser : DeviceParserBase
{
private readonly IHttpClientFactory? _httpClientFactory; // Optional, if HTTP queries needed
public YourDeviceParser(ILogger<YourDeviceParser> logger) : base(logger)
{
// Add dependencies if needed
}
public override bool CanParse(DeviceInfo device)
{
// Use HasServiceType helper - REQUIRED
return HasServiceType(device, "_yourservice");
}
public override Task<object?> QueryDeviceAsync(DeviceInfo device, CancellationToken cancellationToken = default)
{
var info = new YourDeviceInfo { Available = false };
try
{
// 1. Get endpoints using helper
var endpoints = GetEndpointsByServiceType(device, "_yourservice");
if (!endpoints.Any())
{
// 2. Create fallback using helper
endpoints = new List<ServiceEndpoint>
{
CreateFallbackEndpoint(device, "_yourservice._tcp.local.", 8080)
};
}
var primaryEndpoint = endpoints.First();
info.Properties = primaryEndpoint.Properties;
info.Port = primaryEndpoint.Port;
// 3. Parse TXT records using helpers - REQUIRED
info.Name = GetTxtProperty(info.Properties, "name");
info.Version = GetTxtProperty(info.Properties, "version", "ver"); // Fallback keys
info.Enabled = GetTxtBooleanProperty(info.Properties, "enabled");
info.MaxConnections = GetTxtIntProperty(info.Properties, "maxconn") ?? 10;
info.Protocols = GetTxtListProperty(info.Properties, "protocols");
// 4. Query device HTTP endpoint (if applicable)
// var httpClient = CreateHttpClientWithTimeout(_httpClientFactory, "InsecureClient", 2);
// var response = await httpClient.GetAsync(..., cancellationToken);
info.Available = true;
Logger.LogInformation("YourDevice detected at {IP}:{Port}", device.IPAddress, info.Port);
return Task.FromResult<object?>(info);
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to query YourDevice for {DeviceName}", device.Name);
return Task.FromResult<object?>(null);
}
}
}
public class YourDeviceInfo
{
public bool Available { get; set; }
public int Port { get; set; }
public Dictionary<string, string> Properties { get; set; } = new();
public string? Name { get; set; }
public string? Version { get; set; }
public bool Enabled { get; set; }
public int MaxConnections { get; set; }
public List<string> Protocols { get; set; } = new();
}3. Register the parser:
Add to src/mDNSDiscovery.WebApp/Program.cs:
builder.Services.AddSingleton<IDeviceParser, YourDeviceParser>();4. Test your parser:
dotnet build
dotnet run
# Navigate to http://localhost:5000
# Verify your device appears5. Create UI component (optional):
Create src/mDNSDiscovery.WebApp/Components/Shared/YourDeviceInfoSection.razor
REQUIRED patterns:
// ✅ DO: Use HasServiceType
public override bool CanParse(DeviceInfo device)
{
return HasServiceType(device, "_service");
}
// ❌ DON'T: Manual pattern
public override bool CanParse(DeviceInfo device)
{
var allServiceTypes = new List<string> { device.ServiceType };
// ...
}// ✅ DO: Use GetTxtProperty
info.Model = GetTxtProperty(info.Properties, "model");
// ❌ DON'T: Manual check
if (info.Properties.ContainsKey("model"))
info.Model = info.Properties["model"];// ✅ DO: Use type-specific helpers
info.Enabled = GetTxtBooleanProperty(info.Properties, "enabled");
info.Port = GetTxtIntProperty(info.Properties, "port") ?? 80;
info.Formats = GetTxtListProperty(info.Properties, "formats");
// ❌ DON'T: Manual parsing
if (info.Properties.ContainsKey("enabled"))
info.Enabled = info.Properties["enabled"] == "T" || info.Properties["enabled"] == "true";All parsers must have:
- XML summary comment describing the device type
- Service type in comment (e.g.,
_printer._tcp) - Clear variable names
- Comments for complex logic
Example:
/// <summary>
/// Parser for Network Printers (_printer._tcp, _ipp._tcp)
/// </summary>
public class PrinterParser : DeviceParserBaseAll parsers should:
- Wrap QueryDeviceAsync in try-catch
- Log warnings on failure with
Logger.LogWarning - Return null on error
- Log information on success
dotnet build
# Must succeed with 0 warnings, 0 errors# Your parser should appear in these results:
grep -l "HasServiceType" src/mDNSDiscovery.WebApp/Services/Parsers/*.cs
grep -l "GetTxtProperty" src/mDNSDiscovery.WebApp/Services/Parsers/*.cs- Run the application
- Ensure your device appears
- Click device to see details
- Verify all parsed information is correct
- Test with device powered off (should handle gracefully)
- Code builds without warnings or errors
- Follows all code standards above
- Uses DeviceParserBase helpers appropriately
- Includes XML documentation
- Tested manually with actual device
- No code duplication introduced
- What - Description of changes
- Why - Reason for changes
- How - Approach taken
- Testing - How you tested
- Device - What device type this supports
All PRs are reviewed for:
- Code quality (must maintain A+ grade)
- Pattern compliance
- Documentation completeness
- Error handling
- Performance impact
- Parsers:
{DeviceType}Parser(e.g.,PrinterParser) - Info classes:
{DeviceType}Info(e.g.,PrinterInfo) - Services:
{Purpose}Service(e.g.,DeviceQueryService)
Services/
├── Parsers/
│ ├── DeviceParserBase.cs # Base class
│ ├── AirPlayParser.cs # Device-specific parsers
│ └── ...
├── MdnsDiscoveryService.cs # Core services
└── ...
- LogDebug: Verbose details (TXT records, parsing steps)
- LogInformation: Normal operations (device detected)
- LogWarning: Recoverable errors (query failed)
- LogError: Critical errors (service crashed)
public override Task<object?> QueryDeviceAsync(...)
{
var info = new MyInfo { Available = true, Port = device.Port, Properties = device.Properties };
info.Name = GetTxtProperty(info.Properties, "name");
info.Version = GetTxtProperty(info.Properties, "version");
Logger.LogInformation("MyDevice detected at {IP}", device.IPAddress);
return Task.FromResult<object?>(info);
}public override async Task<object?> QueryDeviceAsync(...)
{
try
{
var endpoints = GetEndpointsByServiceType(device, "_service");
if (!endpoints.Any())
{
endpoints = new List<ServiceEndpoint>
{
CreateFallbackEndpoint(device, "_service._tcp.local.", 80)
};
}
var httpClient = CreateHttpClientWithTimeout(_httpClientFactory, "InsecureClient", 3);
var response = await httpClient.GetAsync($"http://{device.IPAddress}/info", cancellationToken);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync(cancellationToken);
// Parse content...
}
return info;
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to query device");
return null;
}
}- mDNS/DNS-SD Specification (RFC 6762)
- DNS-Based Service Discovery (RFC 6763)
- Service Type Registry
- .NET Aspire Documentation
Common service types to implement:
_http._tcp- HTTP services_printer._tcp- IPP printers_airplay._tcp- AirPlay devices_hap._tcp- HomeKit accessories_googlecast._tcp- Chromecast devices_spotify-connect._tcp- Spotify Connect
Find more at: http://www.dns-sd.org/ServiceTypes.html
- Open an issue for questions
- Check existing parsers for examples
- Review the parser guide: PARSER_REFACTORING_GUIDE.md
Thank you for contributing! 🙏